sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1414596 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/io/ main/java/org/apache/sis/math/ main/java/org/apache/sis/util/ main/java/org/apache/sis/util/resources/ test/java/org/apache/sis/math/ test/java/org/apache/sis/te...
Date Wed, 28 Nov 2012 09:03:17 GMT
Author: desruisseaux
Date: Wed Nov 28 09:03:14 2012
New Revision: 1414596

URL: http://svn.apache.org/viewvc?rev=1414596&view=rev
Log:
Ported the Statistics class.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java   (with props)
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java   (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java Wed Nov 28 09:03:14 2012
@@ -148,8 +148,8 @@ public class TableFormatter extends Filt
     /**
      * Alignment for current and next cells.
      *
-     * @see #getAlignment()
-     * @see #setAlignment(byte)
+     * @see #getCellAlignment()
+     * @see #setCellAlignment(byte)
      */
     private byte alignment = ALIGN_LEFT;
 
@@ -370,26 +370,28 @@ public class TableFormatter extends Filt
     }
 
     /**
-     * Returns the alignment for current and next cells.
-     * The default alignment is {@link #ALIGN_LEFT}.
+     * Returns the alignment of the text inside the current cell.
+     * The default value is {@link #ALIGN_LEFT}.
      *
-     * @return Cell alignment: {@link #ALIGN_LEFT} (the default),
-     *         {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
+     * @return Current cell alignment as one of the {@link #ALIGN_LEFT},
+     *         {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER} constants.
      */
-    public byte getAlignment() {
+    public byte getCellAlignment() {
         return alignment;
     }
 
     /**
-     * Sets the alignment for current and next cells. Invoking this method
-     * does does not affect the alignment of previous written cells.
+     * Sets the alignment of the text inside the current cell. The alignments of any cell
+     * written prior this method call are left unchanged. The new alignment will apply to
+     * the next cells too until this {@code setCellAlignment(…)} method is invoked again
+     * with a different value.
      *
-     * <p>The default alignment is {@link #ALIGN_LEFT}.</p>
+     * <p>If this method is never invoked, then the default alignment is {@link #ALIGN_LEFT}.</p>
      *
-     * @param alignment Cell alignment. Must be one of {@link #ALIGN_LEFT}
-     *        {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
+     * @param alignment The new cell alignment as one of the {@link #ALIGN_LEFT},
+     *        {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER} constants.
      */
-    public void setAlignment(final byte alignment) {
+    public void setCellAlignment(final byte alignment) {
         if (alignment < ALIGN_LEFT || alignment > ALIGN_RIGHT) {
             throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.IllegalArgument_1, "alignment"));
@@ -398,35 +400,6 @@ public class TableFormatter extends Filt
     }
 
     /**
-     * Sets the alignment for all cells in the specified column.
-     * The alignments of cells already written prior this method
-     * call are also modified.
-     *
-     * <p>The default alignment is {@link #ALIGN_LEFT}.</p>
-     *
-     * @param column The 0-based column number.
-     * @param alignment Cell alignment. Must be one of {@link #ALIGN_LEFT}
-     *        {@link #ALIGN_RIGHT} or {@link #ALIGN_CENTER}.
-     */
-    public void setColumnAlignment(final int column, final byte alignment) {
-        if (alignment < ALIGN_LEFT || alignment > ALIGN_RIGHT) {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalArgument_1, "alignment"));
-        }
-        int columnIndex = 0;
-        for (final Cell cell : cells) {
-            if (cell == null || cell.text == null) {
-                columnIndex = 0; // New line.
-            } else {
-                if (columnIndex == column) {
-                    cell.alignment = alignment;
-                }
-                columnIndex++;
-            }
-        }
-    }
-
-    /**
      * Returns the number of rows in this table. This count is reset to 0 by {@link #flush()}.
      *
      * @return The number of rows in this table.

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java?rev=1414596&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java Wed Nov 28 09:03:14 2012
@@ -0,0 +1,699 @@
+/*
+ * 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 java.io.Serializable;
+import org.apache.sis.util.Utilities;
+import org.apache.sis.util.ArgumentChecks;
+
+import static java.lang.Math.*;
+import static java.lang.Double.NaN;
+import static java.lang.Double.isNaN;
+import static java.lang.Double.doubleToLongBits;
+
+
+/**
+ * Holds some statistics derived from a series of sample values.
+ * Given a series of <var>y₀</var>, <var>y₁</var>, <var>y₂</var>, <var>y₃</var>, <i>etc…</i> samples,
+ * this class computes the {@linkplain #minimum() minimum}, {@linkplain #maximum() maximum},
+ * {@linkplain #mean() mean}, {@linkplain #rms() root mean square} and
+ * {@linkplain #standardDeviation(boolean) standard deviation} of the given samples.
+ *
+ * <p>In addition to the statistics on the sample values, this class can optionally compute
+ * statistics on the differences between consecutive sample values, i.e. the statistics on
+ * <var>y₁</var>−<var>y₀</var>, <var>y₂</var>−<var>y₁</var>, <var>y₃</var>−<var>y₂</var>, <i>etc…</i>,
+ * Those statistics can be fetched by a call to {@link #differences()}.
+ * They are useful for verifying if the interval between sample values is approximatively constant.</p>
+ *
+ * <p>If the samples are (at least conceptually) the result of some <var>y</var>=<var>f</var>(<var>x</var>)
+ * function for <var>x</var> values increasing or decreasing at a constant interval Δ<var>x</var>,
+ * then one can get the statistics on the <cite>discrete derivatives</cite> by a call to
+ * <code>differences().{@linkplain #scale(double) scale}(1/Δx)</code>.</p>
+ *
+ * <p>Statistics are computed on the fly using the
+ * <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan summation algorithm</a>
+ * for reducing the numerical errors; the sample values are never stored in memory.</p>
+ *
+ * <p>An instance of {@code Statistics} is initially empty: the {@linkplain #count() count} of
+ * values is set to zero, and all above-cited statistical values are set to {@link Double#NaN NaN}.
+ * The statistics are updated every time an {@link #add(double)} method is invoked with a non-NaN
+ * value.</p>
+ *
+ * {@section Examples}
+ * The following examples assume that a <var>y</var>=<var>f</var>(<var>x</var>) function
+ * is defined. A simple usage is:
+ *
+ * {@preformat java
+ *     Statistics stats = new Statistics();
+ *     for (int i=0; i<numberOfValues; i++) {
+ *         stats.add(f(i));
+ *     }
+ *     System.out.println(stats);
+ * }
+ *
+ * Following example computes the statistics on the first and second derivatives
+ * in addition to the statistics on the sample values:
+ *
+ * {@preformat java
+ *     final double x₀ = ...; // Put here the x value at i=0
+ *     final double Δx = ...; // Put here the interval between x values
+ *     Statistics stats = Statistics.forSeries(2);
+ *     for (int i=0; i<numberOfValues; i++) {
+ *         stats.add(f(x₀ + i*Δx));
+ *     }
+ *     stats.differences().scale(1/Δx);
+ *     System.out.println(stats);
+ * }
+ *
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @since   0.3 (derived from geotk-1.0)
+ * @version 0.3
+ * @module
+ */
+public class Statistics implements Cloneable, Serializable {
+    /**
+     * Serial number for compatibility with different versions.
+     */
+    private static final long serialVersionUID = -22884277805533726L;
+
+    /**
+     * The minimal value given to the {@link #add(double)} method.
+     */
+    private double minimum = NaN;
+
+    /**
+     * The maximal value given to the {@link #add(double)} method.
+     */
+    private double maximum = NaN;
+
+    /**
+     * The sum of all values given to the {@link #add(double)} method.
+     */
+    private double sum;
+
+    /**
+     * The sum of square of all values given to the {@link #add(double)} method.
+     */
+    private double squareSum;
+
+    /**
+     * The low-order bits in last update of {@link #sum}.
+     * This is used for the Kahan summation algorithm.
+     */
+    private transient double lowBits;
+
+    /**
+     * The low-order bits in last update of {@link #squareSum}.
+     * This is used for the Kahan summation algorithm.
+     */
+    private transient double squareLowBits;
+
+    /**
+     * Number of non-NaN values given to the {@link #add(double)} method.
+     */
+    private int count;
+
+    /**
+     * Number of NaN values given to the {@link #add(double)} method.
+     * Those value are ignored in the computation of all above values.
+     */
+    private int countNaN;
+
+    /**
+     * Constructs an initially empty set of statistics.
+     * The {@linkplain #count()} and the {@link #sum()} are initialized to zero
+     * and all other statistical values are initialized to {@link Double#NaN}.
+     *
+     * <p>Instances created by this constructor do not compute differences between sample values.
+     * If differences or discrete derivatives are wanted, use the {@link #forSeries(int)} method
+     * instead.</p>
+     */
+    public Statistics() {
+    }
+
+    /**
+     * Constructs a new {@code Statistics} object which will also compute finite differences
+     * up to the given order. If the values to be given to the {@code add(…)} methods are the
+     * <var>y</var> values of some <var>y</var>=<var>f</var>(<var>x</var>) function for
+     * <var>x</var> values increasing or decreasing at a constant interval Δ<var>x</var>,
+     * then the finite differences are proportional to discrete derivatives.
+     *
+     * <p>The {@code Statistics} object created by this method know nothing about the Δ<var>x</var>
+     * interval. In order to get the discrete derivatives, the following method needs to be invoked
+     * <em>after</em> all sample values have been added:</p>
+     *
+     * {@preformat java
+     *     statistics.differences().scale(1/Δx);
+     * }
+     *
+     * The {@code order} argument can be interpreted as below:
+     *
+     * <ul>
+     *   <li>0 if no differences are needed;</li>
+     *   <li>1 for computing the statistics on the differences between consecutive samples
+     *       (proportional to the statistics on the first discrete derivatives) in addition
+     *       to the sample statistics;</li>
+     *   <li>2 for computing also the statistics on the differences between consecutive differences
+     *       (proportional to the statistics on the second discrete derivatives) in addition to the
+     *       above;</li>
+     *   <li><i>etc</i>.</li>
+     * </ul>
+     *
+     * @param  order The discrete derivative order, as a value equals or greater than 0.
+     * @return The newly constructed, initially empty, set of statistics.
+     *
+     * @see #differences()
+     */
+    public static Statistics forSeries(int order) {
+        ArgumentChecks.ensurePositive("order", order);
+        Statistics stats = new Statistics();
+        while (--order >= 0) {
+            stats = new WithDelta(stats);
+        }
+        return stats;
+    }
+
+    /**
+     * Resets this object state as if it was just created.
+     * The {@linkplain #count()} and the {@link #sum()} are set to zero
+     * and all other statistical values are set to {@link Double#NaN}.
+     */
+    public void reset() {
+        minimum       = NaN;
+        maximum       = NaN;
+        sum           = 0;
+        squareSum     = 0;
+        lowBits       = 0;
+        squareLowBits = 0;
+        count         = 0;
+        countNaN      = 0;
+    }
+
+    /**
+     * Updates statistics for the specified floating-point sample value.
+     * {@link Double#NaN NaN} values increment the {@linkplain #countNaN() NaN count},
+     * but are otherwise ignored.
+     *
+     * @param sample The sample value (may be NaN).
+     *
+     * @see #add(long)
+     * @see #add(Statistics)
+     */
+    public void add(final double sample) {
+        if (isNaN(sample)) {
+            countNaN++;
+        } else {
+            addReal(sample);
+        }
+    }
+
+    /**
+     * Implementation of {@link #add(double)} for real (non-NaN) numbers.
+     */
+    private void addReal(double sample) {
+        // Two next lines use !(a >= b) instead than
+        // (a < b) in order to take NaN in account.
+        if (!(minimum <= sample)) minimum = sample;
+        if (!(maximum >= sample)) maximum = sample;
+
+        // According algebraic laws, lowBits should always been zero. But it is
+        // not when using floating points with limited precision. Do not simplify!
+        double y = sample + lowBits;
+        lowBits = y + (sum - (sum += y));
+
+        sample *= sample;
+        y = sample + squareLowBits;
+        squareLowBits = y + (squareSum - (squareSum += y));
+
+        count++;
+    }
+
+    /**
+     * Updates statistics for the specified integer sample value.
+     * For very large integer values (greater than 2<sup>52</sup> in magnitude),
+     * this method may be more accurate than the {@link #add(double)} version.
+     *
+     * @param sample The sample value.
+     *
+     * @see #add(double)
+     * @see #add(Statistics)
+     */
+    public void add(final long sample) {
+        addReal(sample);
+    }
+
+    /**
+     * Updates statistics with all samples from the specified {@code stats}.
+     * Invoking this method is equivalent (except for rounding errors) to invoking
+     * {@link #add(double) add} for all samples that were added to {@code stats}.
+     *
+     * @param stats The statistics to be added to {@code this}.
+     */
+    public void add(final Statistics stats) {
+        ArgumentChecks.ensureNonNull("stats", stats);
+
+        // "if (a < b)" is equivalent to "if (!isNaN(a) && a < b)".
+        if (isNaN(minimum) || stats.minimum < minimum) minimum = stats.minimum;
+        if (isNaN(maximum) || stats.maximum > maximum) maximum = stats.maximum;
+
+        double y = stats.sum + lowBits;
+        lowBits = y + (sum - (sum += y)) + stats.lowBits;
+
+        y = stats.squareSum + squareLowBits;
+        squareLowBits = y + (squareSum - (squareSum += y)) + stats.squareLowBits;
+
+        count += stats.count;
+        countNaN += max(stats.countNaN, 0);
+    }
+
+    /**
+     * Multiplies the statistics by the given factor. The given scale factory is also applied
+     * recursively on the {@linkplain #differences() differences} statistics, if any.
+     * Invoking this method transforms the statistics as if every values given to the
+     * {@code add(…)} had been first multiplied by the given factor.
+     *
+     * <p>This method is useful for computing discrete derivatives from the differences between
+     * sample values. See {@link #differences()} or {@link #forSeries(int)} for more information.</p>
+     *
+     * @param factor The factor by which to multiply the statistics.
+     */
+    public void scale(double factor) {
+        ArgumentChecks.ensureFinite("factor", factor);
+        minimum       *= factor;
+        maximum       *= factor;
+        sum           *= factor;
+        lowBits       *= factor;
+        factor        *= factor;
+        squareSum     *= factor;
+        squareLowBits *= factor;
+    }
+
+    /**
+     * For {@link WithDelta} usage only.
+     */
+    void decrementCountNaN() {
+        countNaN--;
+    }
+
+    /**
+     * Returns the number of {@link Double#NaN NaN} samples.
+     * {@code NaN} samples are ignored in all other statistical computation.
+     * This method count them for information purpose only.
+     *
+     * @return The number of NaN values.
+     */
+    public int countNaN() {
+        return max(countNaN, 0); // The Delta subclass initializes countNaN to -1.
+    }
+
+    /**
+     * Returns the number of samples, excluding {@link Double#NaN NaN} values.
+     *
+     * @return The number of sample values, excluding NaN.
+     */
+    public int count() {
+        return count;
+    }
+
+    /**
+     * Returns the minimum sample value, or {@link Double#NaN NaN} if none.
+     *
+     * @return The minimum sample value, or NaN if none.
+     */
+    public double minimum() {
+        return minimum;
+    }
+
+    /**
+     * Returns the maximum sample value, or {@link Double#NaN NaN} if none.
+     *
+     * @return The maximum sample value, or NaN if none.
+     */
+    public double maximum() {
+        return maximum;
+    }
+
+    /**
+     * Equivalents to <code>{@link #maximum() maximum} - {@link #minimum() minimum}</code>,
+     * If no samples were added, then returns {@link Double#NaN NaN}.
+     *
+     * @return The span of sample values, or NaN if none.
+     */
+    public double span() {
+        return maximum - minimum;
+    }
+
+    /**
+     * Returns the sum, or 0 if none.
+     *
+     * @return The sum, or 0 if none.
+     */
+    public double sum() {
+        return sum;
+    }
+
+    /**
+     * Returns the mean value, or {@link Double#NaN NaN} if none.
+     *
+     * @return The mean value, or NaN if none.
+     */
+    public double mean() {
+        return sum / count;
+    }
+
+    /**
+     * Returns the root mean square, or {@link Double#NaN NaN} if none.
+     *
+     * @return The root mean square, or NaN if none.
+     */
+    public double rms() {
+        return sqrt(squareSum / count);
+    }
+
+    /**
+     * Returns the standard deviation. If the sample values given to the {@code add(…)}
+     * methods have a uniform distribution, then the returned value should be close to
+     * <code>sqrt({@linkplain #span() span}<sup>2</sup> / 12)</code>. If they have a
+     * Gaussian distribution (which is the most common case), then the returned value
+     * is related to the <a href="http://en.wikipedia.org/wiki/Error_function">error
+     * function</a>.
+     *
+     * <p>As a reminder, the table below gives the probability for a sample value to be
+     * inside the {@linkplain #mean() mean} ± <var>n</var> × <var>deviation range</var>,
+     * assuming that the distribution is Gaussian (first column) or assuming that the
+     * distribution is uniform (second column).</p>
+     *
+     * <table class="sis">
+     *   <tr><th>n</th><th>Gaussian</th><th>uniform</th>
+     *   <tr><td>0.5</td><td>69.1%</td><td>28.9%</td></tr>
+     *   <tr><td>1.0</td><td>84.2%</td><td>57.7%</td></tr>
+     *   <tr><td>1.5</td><td>93.3%</td><td>86.6%</td></tr>
+     *   <tr><td>2.0</td><td>97.7%</td><td>100%</td></tr>
+     *   <tr><td>3.0</td><td>99.9%</td><td>100%</td></tr>
+     * </table>
+     *
+     * @param allPopulation
+     *          {@code true} if sample values given to {@code add(…)} methods were the totality
+     *          of the population under study, or {@code false} if they were only a sampling.
+     * @return  The standard deviation.
+     */
+    public double standardDeviation(final boolean allPopulation) {
+        return sqrt((squareSum - sum*sum/count) / (allPopulation ? count : count-1));
+    }
+
+    /**
+     * Returns the statistics on the differences between sample values, or {@code null} if none.
+     * For example if the sample values given to the {@code add(…)} methods were <var>y₀</var>,
+     * <var>y₁</var>, <var>y₂</var> and <var>y₃</var>, then this method returns statistics on
+     * <var>y₁</var>−<var>y₀</var>, <var>y₂</var>−<var>y₁</var> and <var>y₃</var>−<var>y₂</var>.
+     *
+     * <p>The differences between sample values are related to the discrete derivatives as below,
+     * where Δ<var>x</var> is the constant interval between the <var>x</var> values of the
+     * <var>y</var>=<var>f</var>(<var>x</var>) function:</p>
+     *
+     * {@preformat java
+     *     Statistics derivative = statistics.differences();
+     *     derivative.scale(1/Δx); // Shall be invoked only once.
+     *     Statistics secondDerivative = derivative.differences();
+     *     // Do not invoke scale(1/Δx) again.
+     * }
+     *
+     * This method is supported only if this {@code Statistics} instance has been created by a
+     * call to the {@link #forSeries(int)} method with an {@code order} argument greater than zero.
+     * More generally, calls to this method can be chained up to {@code order} time for
+     * fetching second or higher order derivatives, as in the above example.
+     *
+     * @return The statistics on the differences between consecutive sample values,
+     *         or {@code null} if not calculated by this object.
+     *
+     * @see #forSeries(int)
+     * @see #scale(double)
+     */
+    public Statistics differences() {
+        return null;
+    }
+
+    /**
+     * Returns a string representation of this statistics. This string will span
+     * multiple lines, one for each statistical value. For example:
+     *
+     * {@preformat text
+     *     Count:                8726
+     *     Minimum:             6.853
+     *     Maximum:             8.259
+     *     Mean:                7.421
+     *     Root Mean Square:    7.846
+     *     Standard deviation:  6.489
+     * }
+     *
+     * @return A string representation of this statistics object.
+     */
+    @Override
+    public String toString() {
+        return StatisticsFormat.getInstance().format(this);
+    }
+
+    /**
+     * Returns a clone of this statistics.
+     *
+     * @return A clone of this statistics.
+     */
+    @Override
+    public Statistics clone() {
+        try {
+            return (Statistics) super.clone();
+        } catch (CloneNotSupportedException exception) {
+            // Should not happen since we are cloneable
+            throw new AssertionError(exception);
+        }
+    }
+
+    /**
+     * Returns a hash code value for this statistics.
+     */
+    @Override
+    public int hashCode() {
+        final long code = (doubleToLongBits(minimum) +
+                     31 * (doubleToLongBits(maximum) +
+                     31 * (doubleToLongBits(sum) +
+                     31 * (doubleToLongBits(squareSum)))));
+        return (int) code ^ (int) (code >>> 32) ^ count;
+    }
+
+    /**
+     * Compares this statistics with the specified object for equality.
+     *
+     * @param  object The object to compare with.
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object object) {
+        if (object != null && getClass() == object.getClass()) {
+            final Statistics cast = (Statistics) object;
+            return count == cast.count && countNaN == cast.countNaN
+                    && Utilities.equals(minimum,   cast.minimum)
+                    && Utilities.equals(maximum,   cast.maximum)
+                    && Utilities.equals(sum,       cast.sum)
+                    && Utilities.equals(squareSum, cast.squareSum);
+        }
+        return false;
+    }
+
+
+
+
+    /**
+     * Holds some statistics about the difference between consecutive sample values.
+     * Given a series of <var>s₀</var>, <var>s₁</var>, <var>s₂</var>, <var>s₃</var>,
+     * <i>etc…</i> samples, this class computes statistics for
+     * <var>s₁</var>−<var>s₀</var>,
+     * <var>s₂</var>−<var>s₁</var>,
+     * <var>s₃</var>−<var>s₂</var>, <i>etc…</i>
+     * which are stored in a {@link #delta} statistics object.
+     *
+     * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+     * @since   0.3 (derived from geotk-1.0)
+     * @version 0.3
+     * @module
+     */
+    private static final class WithDelta extends Statistics {
+        /**
+         * Serial number for compatibility with different versions.
+         */
+        private static final long serialVersionUID = 3464306833883333219L;
+
+        /**
+         * Statistics about the differences between consecutive sample values.
+         * Consider this field as final; it is modified only by the {@link #clone()} method.
+         */
+        private Statistics delta;
+
+        /**
+         * Last value given to an {@link #add(double) add} method as
+         * a {@code double}, or {@link Double#NaN NaN} if none.
+         */
+        private double last = NaN;
+
+        /**
+         * Last value given to an {@link #add(long) add}
+         * method as a {@code long}, or 0 if none.
+         */
+        private long lastAsLong;
+
+        /**
+         * Constructs an initially empty set of statistics using the specified object for
+         * {@link #delta} statistics. This constructor allows chaining different kind of
+         * statistics objects. For example, one could write:
+         *
+         * {@preformat java
+         *     new Statistics.Delta(new Statistics.Delta());
+         * }
+         *
+         * which would compute statistics of sample values, statistics of difference between
+         * consecutive sample values, and statistics of difference of difference between
+         * consecutive sample values. Other kinds of {@link Statistics} object could be
+         * chained as well.
+         *
+         * @param delta The object where to stores delta statistics.
+         */
+        public WithDelta(final Statistics delta) {
+            this.delta = delta;
+            delta.decrementCountNaN(); // Do not count the first NaN, which will always be the first value.
+        }
+
+        /**
+         * Resets this object state as if it was just created.
+         */
+        @Override
+        public void reset() {
+            super.reset();
+            delta.reset();
+            delta.decrementCountNaN(); // Do not count the first NaN, which will always be the first value.
+            last       = NaN;
+            lastAsLong = 0;
+        }
+
+        /**
+         * Updates statistics for the specified sample value and its discrete derivatives.
+         * The {@link #delta} statistics are updated with <code>sample − sample<sub>last</sub></code>
+         * value, where <code>sample<sub>last</sub></code> is the value given to the previous call of
+         * an {@code add(…)} method.
+         */
+        @Override
+        public void add(final double sample) {
+            super.add(sample);
+            delta.add(sample - last);
+            last       = sample;
+            lastAsLong = (long) sample;
+        }
+
+        /**
+         * Performs the same work than {@link #add(double)}, but with greater precision for
+         * very large integer values (greater than 2<sup>52</sup> in magnitude),
+         */
+        @Override
+        public void add(final long sample) {
+            super.add(sample);
+            if (last == (double) lastAsLong) {
+                // 'lastAsLong' may have more precision than 'last' since the cast to the
+                // 'double' type may loose some digits. Invoke the 'delta.add(long)' version.
+                delta.add(sample - lastAsLong);
+            } else {
+                // The sample value is either fractional, outside 'long' range,
+                // infinity or NaN. Invoke the 'delta.add(double)' version.
+                delta.add(sample - last);
+            }
+            last       = sample;
+            lastAsLong = sample;
+        }
+
+        /**
+         * Update statistics with all samples from the specified {@code stats}.
+         *
+         * @throws ClassCastException If {@code stats} is not an instance of
+         *         {@code Statistics.Delta}.
+         */
+        @Override
+        public void add(final Statistics stats) throws ClassCastException {
+            ArgumentChecks.ensureNonNull("stats", stats);
+            delta.add(stats.differences());
+            super.add(stats);
+            if (stats instanceof WithDelta) {
+                final WithDelta toAdd = (WithDelta) stats;
+                last       = toAdd.last;
+                lastAsLong = toAdd.lastAsLong;
+            } else {
+                last       = NaN;
+                lastAsLong = 0;
+            }
+        }
+
+        /**
+         * Scales the statistics by the given factor.
+         */
+        @Override
+        public void scale(final double factor) {
+            super.scale(factor);
+            delta.scale(factor);
+        }
+
+        /**
+         * Decrements the count of NaN values by one. This method is invoked on construction
+         * or on {@link #reset()} call, because the first discrete derivative always have one
+         * less value than the original one, the second derivative two less values, etc.
+         */
+        @Override
+        final void decrementCountNaN() {
+            super.decrementCountNaN();
+            delta.decrementCountNaN();
+        }
+
+        /**
+         * Returns the statistics about difference between consecutive values.
+         */
+        @Override
+        public Statistics differences() {
+            return delta;
+        }
+
+        /**
+         * Returns a clone of this statistics.
+         */
+        @Override
+        public Statistics clone() {
+            final WithDelta copy = (WithDelta) super.clone();
+            copy.delta = copy.delta.clone();
+            return copy;
+        }
+
+        /**
+         * Tests this statistics with the specified object for equality.
+         */
+        @Override
+        public boolean equals(final Object obj) {
+            return super.equals(obj) && delta.equals(((WithDelta) obj).delta);
+        }
+
+        /**
+         * Returns a hash code value for this statistics.
+         */
+        @Override
+        public int hashCode() {
+            return super.hashCode() + 31*delta.hashCode();
+        }
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java?rev=1414596&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java Wed Nov 28 09:03:14 2012
@@ -0,0 +1,271 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.DecimalFormat;
+import java.text.ParsePosition;
+import java.text.ParseException;
+
+import org.apache.sis.io.TableFormatter;
+import org.apache.sis.io.CompoundFormat;
+import org.apache.sis.util.resources.Vocabulary;
+
+import static java.lang.Math.*;
+
+
+/**
+ * Formats a {@link Statistics} object.
+ * This is a package-private class for now - if we want to make it public, we may need to make it
+ * a full-featured {@link java.text.Format} implementation.
+ *
+ * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
+ * @since   0.3 (derived from geotk-1.0)
+ * @version 0.3
+ * @module
+ */
+@SuppressWarnings("serial")
+final class StatisticsFormat extends CompoundFormat<Statistics> {
+    /**
+     * Number of additional digits, to be added to the number of digits computed from the
+     * range and the number of sample values. This is an arbitrary parameter.
+     */
+    private static final int ADDITIONAL_DIGITS = 2;
+
+    /**
+     * {@code true} if the sample values given to {@code Statistics.add(…)} methods were the
+     * totality of the population under study, or {@code false} if they were only a sampling.
+     *
+     * @see Statistics#standardDeviation(boolean)
+     */
+    private boolean allPopulation;
+
+    /**
+     * The column separator, typically as a semicolon or tabulation character.
+     * If 0, then the values will be written in a tabular format using {@link TableFormatter}.
+     */
+    private char columnSeparator;
+
+    /**
+     * Returns an instance for the current system default locale.
+     *
+     * @return A statistics format instance for the current default locale.
+     */
+    public static StatisticsFormat getInstance() {
+        return new StatisticsFormat(Locale.getDefault(Locale.Category.FORMAT), null);
+    }
+
+    /**
+     * Constructs a new format for the given locale.
+     * The timezone is used only if the values added to the {@link Statistics} are dates.
+     *
+     * @param locale   The locale, or {@code null} for unlocalized format.
+     * @param timezone The timezone, or {@code null} for UTC.
+     */
+    public StatisticsFormat(final Locale locale, final TimeZone timezone) {
+        super(locale, timezone);
+    }
+
+    /**
+     * Returns the kind of objects formatted by this class.
+     */
+    @Override
+    public Class<Statistics> getValueType() {
+        return Statistics.class;
+    }
+
+    /**
+     * Not yet implemented.
+     */
+    @Override
+    public Statistics parse(CharSequence text, ParsePosition pos) throws ParseException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The resource keys of the rows to formats. Array index must be consistent with
+     * the switch statements inside the {@link #format(Statistics)} method.
+     */
+    private static final int[] KEYS = {
+        Vocabulary.Keys.NumberOfValues,
+        Vocabulary.Keys.NumberOfNaN,
+        Vocabulary.Keys.MinimumValue,
+        Vocabulary.Keys.MaximumValue,
+        Vocabulary.Keys.MeanValue,
+        Vocabulary.Keys.RootMeanSquare,
+        Vocabulary.Keys.StandardDeviation
+    };
+
+    /**
+     * Formats a localized string representation of the given statistics.
+     * If statistics on {@linkplain Statistics#differences() differences}
+     * are associated to the given object, they will be formatted too.
+     */
+    @Override
+    public void format(Statistics stats, final Appendable toAppendTo) throws IOException {
+        final List<Statistics> list = new ArrayList<>(3);
+        while (stats != null) {
+            list.add(stats);
+            stats = stats.differences();
+        }
+        format(list.toArray(new Statistics[list.size()]), toAppendTo);
+    }
+
+    /**
+     * Formats the given statistics in a tabular format. This method does not check
+     * for the statistics on {@linkplain Statistics#differences() differences} - if
+     * such statistics are wanted, they must be included in the given array.
+     */
+    private void format(final Statistics[] stats, Appendable toAppendTo) throws IOException {
+        final String lineSeparator = System.lineSeparator();
+        /*
+         * Verify if we can omit the count of NaN values.
+         */
+        boolean showNaNCount = false;
+        for (final Statistics s : stats) {
+            if (s.countNaN() != 0) {
+                showNaNCount = true;
+                break;
+            }
+        }
+        /*
+         * This formatter can optionally use a column separator, typically a semi-colon.
+         * If no column separator was specified (which is the usual case), then we will
+         * format the values in a tabular format.
+         */
+        TableFormatter table = null;
+        char separator = columnSeparator;
+        if (separator == 0) {
+            toAppendTo = table = new TableFormatter(toAppendTo, " ");
+            separator = '\t'; // Will be handled especially by TableFormatter.
+        }
+        final Vocabulary resources = Vocabulary.getResources(getLocale());
+        /*
+         * Initialize the NumberFormat for formatting integers without scientific notation.
+         * This is necessary since the format may have been modified by a previous execution
+         * of this method.
+         */
+        final Format format = getFormat(Double.class);
+        if (format instanceof DecimalFormat) {
+            ((DecimalFormat) format).applyPattern("#0"); // Also disable scientific notation.
+        } else if (format instanceof NumberFormat) {
+            setFractionDigits((NumberFormat) format, 0);
+        }
+        /*
+         * Iterates over the rows to format (count, minimum, maximum, mean, RMS, standard deviation),
+         * then iterate over columns (statistics on sample values, on the first derivatives, etc.)
+         * The NumberFormat configuration may be different for each column, but we can skip many
+         * reconfiguration in the common case where there is only one column.
+         */
+        boolean needsConfigure = false;
+        for (int i=0; i<KEYS.length; i++) {
+            switch (i) {
+                case 1: if (!showNaNCount) continue; else break;
+                // Case 0 and 1 use the above configuration for integers.
+                // Case 2 unconditionally needs a reconfiguration for floating point values.
+                // Case 3 and others need reconfiguration only if there is more than one column.
+                case 2: needsConfigure = true; break;
+                case 3: needsConfigure = (stats[0].differences() != null); break;
+            }
+            if (table != null) {
+                table.setCellAlignment(TableFormatter.ALIGN_LEFT);
+            }
+            toAppendTo.append(resources.getString(KEYS[i])).append(':');
+            for (final Statistics s : stats) {
+                final Number value;
+                switch (i) {
+                    case 0:  value = s.count();    break;
+                    case 1:  value = s.countNaN(); break;
+                    case 2:  value = s.minimum();  break;
+                    case 3:  value = s.maximum();  break;
+                    case 4:  value = s.mean();     break;
+                    case 5:  value = s.rms();      break;
+                    case 6:  value = s.standardDeviation(allPopulation); break;
+                    default: throw new AssertionError(i);
+                }
+                if (needsConfigure) {
+                    configure(format, s);
+                }
+                toAppendTo.append(separator).append(format.format(value));
+                if (table != null) {
+                    table.setCellAlignment(TableFormatter.ALIGN_RIGHT);
+                }
+            }
+            toAppendTo.append(lineSeparator);
+        }
+        /*
+         * TableFormatter needs to be explicitly flushed in order to format the values.
+         */
+        if (table != null) {
+            table.flush();
+        }
+    }
+
+    /**
+     * Configures the given formatter for writing a set of data described by the given statistics.
+     * This method configures the formatter using heuristic rules based on the range of values and
+     * their standard deviation. It can be used for reasonable default formatting when the user
+     * didn't specify an explicit one.
+     *
+     * @param  format The formatter to configure.
+     * @param  stats  The statistics for which to configure the formatter.
+     */
+    private void configure(final Format format, final Statistics stats) {
+        final double minimum  = stats.minimum();
+        final double maximum  = stats.maximum();
+        final double extremum = max(abs(minimum), abs(maximum));
+        if ((extremum >= 1E+10 || extremum <= 1E-4) && format instanceof DecimalFormat) {
+            /*
+             * The above threshold is high so that geocentric and projected coordinates in metres
+             * are not formatted with scientific notation (a threshold of 1E+7 is not enough).
+             * The number of decimal digits in the pattern is arbitrary.
+             */
+            ((DecimalFormat) format).applyPattern("0.00000E00");
+        } else {
+            /*
+             * Computes a representative range of values. We take 2 standard deviations away
+             * from the mean. Assuming that data have a gaussian distribution, this is 97.7%
+             * of data. If the data have a uniform distribution, then this is 100% of data.
+             */
+            double delta;
+            final double mean = stats.mean();
+            delta = 2 * stats.standardDeviation(true); // 'true' is for avoiding NaN when count == 1.
+            delta = min(maximum, mean+delta) - max(minimum, mean-delta); // Range of 97.7% of values.
+            delta = max(delta/stats.count(), ulp(extremum)); // Mean delta for uniform distribution, not finer than 'double' accuracy.
+            if (format instanceof NumberFormat) {
+                setFractionDigits((NumberFormat) format, max(0, ADDITIONAL_DIGITS
+                        + MathFunctions.fractionDigitsForDelta(delta, false)));
+            } else {
+                // A future version could configure DateFormat here.
+            }
+        }
+    }
+
+    /**
+     * Convenience method for setting the minimum and maximum fraction digits of the given format.
+     */
+    private static void setFractionDigits(final NumberFormat format, final int digits) {
+        format.setMinimumFractionDigits(digits);
+        format.setMaximumFractionDigits(digits);
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java Wed Nov 28 09:03:14 2012
@@ -238,12 +238,12 @@ public final class ArgumentChecks extend
 
     /**
      * Ensures that the given floating point value is not
-     * {@linkplain Float#NaN NaN} and is greater than or equals to zero. Note that
+     * {@linkplain Float#isNaN(float) NaN} and is greater than or equals to zero. Note that
      * {@linkplain Float#POSITIVE_INFINITY positive infinity} is considered a valid value.
      *
      * @param  name   The name of the argument to be checked, used only if an exception is thrown.
      * @param  value  The user argument to check.
-     * @throws IllegalArgumentException if the given value is {@linkplain Float#NaN NaN} or negative.
+     * @throws IllegalArgumentException if the given value is NaN or negative.
      *
      * @see #ensureStrictlyPositive(String, float)
      */
@@ -259,12 +259,12 @@ public final class ArgumentChecks extend
 
     /**
      * Ensures that the given floating point value is not
-     * {@linkplain Double#NaN NaN} and is greater than or equals to zero. Note that
+     * {@linkplain Double#isNaN(double) NaN} and is greater than or equals to zero. Note that
      * {@linkplain Double#POSITIVE_INFINITY positive infinity} is considered a valid value.
      *
      * @param  name   The name of the argument to be checked, used only if an exception is thrown.
      * @param  value  The user argument to check.
-     * @throws IllegalArgumentException if the given value is {@linkplain Double#NaN NaN} or negative.
+     * @throws IllegalArgumentException if the given value is NaN or negative.
      *
      * @see #ensureStrictlyPositive(String, double)
      */
@@ -316,13 +316,12 @@ public final class ArgumentChecks extend
 
     /**
      * Ensures that the given floating point value is not
-     * {@linkplain Float#NaN NaN} and is greater than zero. Note that
+     * {@linkplain Float#isNaN(float) NaN} and is greater than zero. Note that
      * {@linkplain Float#POSITIVE_INFINITY positive infinity} is considered a valid value.
      *
      * @param  name   The name of the argument to be checked, used only if an exception is thrown.
      * @param  value  The user argument to check.
-     * @throws IllegalArgumentException if the given value is {@linkplain Float#NaN NaN},
-     *         zero or negative.
+     * @throws IllegalArgumentException if the given value is NaN, zero or negative.
      *
      * @see #ensurePositive(String, float)
      */
@@ -338,13 +337,12 @@ public final class ArgumentChecks extend
 
     /**
      * Ensures that the given floating point value is not
-     * {@linkplain Double#NaN NaN} and is greater than zero. Note that
+     * {@linkplain Double#isNaN(double) NaN} and is greater than zero. Note that
      * {@linkplain Double#POSITIVE_INFINITY positive infinity} is considered a valid value.
      *
      * @param  name   The name of the argument to be checked, used only if an exception is thrown.
      * @param  value  The user argument to check.
-     * @throws IllegalArgumentException if the given value is {@linkplain Double#NaN NaN},
-     *         zero or negative.
+     * @throws IllegalArgumentException if the given value is NaN, zero or negative.
      *
      * @see #ensurePositive(String, double)
      */
@@ -359,6 +357,40 @@ public final class ArgumentChecks extend
     }
 
     /**
+     * Ensures that the given floating point value is not
+     * {@linkplain Float#isNaN(float) NaN} neither {@linkplain Float#isInfinite(float)}.
+     * The value can be negative, zero or positive.
+     *
+     * @param  name   The name of the argument to be checked, used only if an exception is thrown.
+     * @param  value  The user argument to check.
+     * @throws IllegalArgumentException if the given value is NaN or infinite.
+     */
+    public static void ensureFinite(final String name, final float value) {
+        final boolean isNaN;
+        if ((isNaN = Float.isNaN(value)) == true || Float.isInfinite(value)) {
+            throw new IllegalArgumentException(Errors.format(isNaN ?
+                    Errors.Keys.NotANumber_1 : Errors.Keys.InfiniteArgumentValue_1, name));
+        }
+    }
+
+    /**
+     * Ensures that the given floating point value is not
+     * {@linkplain Double#isNaN(double) NaN} neither {@linkplain Double#isInfinite(double)}.
+     * The value can be negative, zero or positive.
+     *
+     * @param  name   The name of the argument to be checked, used only if an exception is thrown.
+     * @param  value  The user argument to check.
+     * @throws IllegalArgumentException if the given value is NaN or infinite.
+     */
+    public static void ensureFinite(final String name, final double value) {
+        final boolean isNaN;
+        if ((isNaN = Double.isNaN(value)) == true || Double.isInfinite(value)) {
+            throw new IllegalArgumentException(Errors.format(isNaN ?
+                    Errors.Keys.NotANumber_1 : Errors.Keys.InfiniteArgumentValue_1, name));
+        }
+    }
+
+    /**
      * Ensures that the given integer value is between the given bounds, inclusive.
      *
      * @param  name  The name of the argument to be checked. Used only in case an exception is thrown.
@@ -403,8 +435,7 @@ public final class ArgumentChecks extend
      * @param  min   The minimal value, inclusive.
      * @param  max   The maximal value, inclusive.
      * @param  value The value to be tested.
-     * @throws IllegalArgumentException if the given value is {@linkplain Float#NaN NaN}
-     *         or not in the given range.
+     * @throws IllegalArgumentException if the given value is NaN or not in the given range.
      */
     public static void ensureBetween(final String name, final float min, final float max, final float value)
             throws IllegalArgumentException
@@ -423,8 +454,7 @@ public final class ArgumentChecks extend
      * @param  min   The minimal value, inclusive.
      * @param  max   The maximal value, inclusive.
      * @param  value The value to be tested.
-     * @throws IllegalArgumentException if the given value is {@linkplain Float#NaN NaN}
-     *         or not in the given range.
+     * @throws IllegalArgumentException if the given value is NaN or not in the given range.
      */
     public static void ensureBetween(final String name, final double min, final double max, final double value)
             throws IllegalArgumentException

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java Wed Nov 28 09:03:14 2012
@@ -54,6 +54,11 @@ public final class Errors extends Indexe
         }
 
         /**
+         * Can not compute the derivative.
+         */
+        public static final int CanNotComputeDerivative = 44;
+
+        /**
          * Can not clone an object of type ‘{0}’.
          */
         public static final int CloneNotSupported_1 = 42;
@@ -145,6 +150,11 @@ public final class Errors extends Indexe
         public static final int IndexOutOfBounds_1 = 4;
 
         /**
+         * Argument ‘{0}’ can not take an infinite value.
+         */
+        public static final int InfiniteArgumentValue_1 = 45;
+
+        /**
          * A different value is already associated to the “{0}” key.
          */
         public static final int KeyCollision_1 = 19;

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties Wed Nov 28 09:03:14 2012
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+CanNotComputeDerivative         = Can not compute the derivative.
 CloneNotSupported_1             = Can not clone an object of type \u2018{0}\u2019.
 DeadThread_1                    = Thread \u201c{0}\u201d is dead.
 DuplicatedValue_1               = Value \u201c{0}\u201d is duplicated.
@@ -32,6 +33,7 @@ IllegalRange_2                  = Range 
 InconsistentAttribute_2         = Value \u201c{1}\u201d of attribute \u2018{0}\u2019 is inconsistent with other attributes.
 InconsistentTableColumns        = Inconsistent table columns.
 IndexOutOfBounds_1              = Index {0} is out of bounds.
+InfiniteArgumentValue_1         = Argument \u2018{0}\u2019 can not take an infinite value.
 KeyCollision_1                  = A different value is already associated to the \u201c{0}\u201d key.
 MandatoryAttribute_2            = Attribute \u201c{0}\u201d is mandatory for an object of type \u2018{1}\u2019.
 NegativeArgument_2              = Argument \u2018{0}\u2019 shall not be negative. The given value was {1}.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties Wed Nov 28 09:03:14 2012
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+CanNotComputeDerivative         = La d\u00e9riv\u00e9 ne peut pas \u00eatre calcul\u00e9e.
 CloneNotSupported_1             = Un objet de type \u2018{0}\u2019 ne peut pas \u00eatre clon\u00e9.
 DeadThread_1                    = La t\u00e2che \u201c{0}\u201d est morte.
 DuplicatedValue_1               = La valeur \u201c{0}\u201d est dupliqu\u00e9e.
@@ -32,6 +33,7 @@ IllegalRange_2                  = La pla
 InconsistentAttribute_2         = La valeur \u201c{1}\u201d de l\u2019attribut \u2018{0}\u2019 n\u2019est pas coh\u00e9rente avec celles des autres attributs.
 InconsistentTableColumns        = Les colonnes des tables ne sont pas coh\u00e9rentes.
 IndexOutOfBounds_1              = L\u2019index {0} est en dehors des limites permises.
+InfiniteArgumentValue_1         = L\u2019argument \u2018{0}\u2019 ne peut pas prendre une valeur infinie.
 KeyCollision_1                  = Une valeur diff\u00e9rente est d\u00e9j\u00e0 associ\u00e9e \u00e0 la cl\u00e9 \u201c{0}\u201d.
 MandatoryAttribute_2            = L\u2019attribut \u201c{0}\u201d est obligatoire pour un objet de type \u2018{1}\u2019.
 NegativeArgument_2              = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre n\u00e9gatif. La valeur donn\u00e9e \u00e9tait {1}.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java Wed Nov 28 09:03:14 2012
@@ -54,11 +54,46 @@ public final class Vocabulary extends In
         }
 
         /**
+         * Maximum value
+         */
+        public static final int MaximumValue = 5;
+
+        /**
+         * Mean value
+         */
+        public static final int MeanValue = 6;
+
+        /**
+         * Minimum value
+         */
+        public static final int MinimumValue = 4;
+
+        /**
          * Name
          */
         public static final int Name = 0;
 
         /**
+         * Number of ‘NaN’
+         */
+        public static final int NumberOfNaN = 3;
+
+        /**
+         * Number of values
+         */
+        public static final int NumberOfValues = 2;
+
+        /**
+         * Root Mean Square
+         */
+        public static final int RootMeanSquare = 7;
+
+        /**
+         * Standard deviation
+         */
+        public static final int StandardDeviation = 8;
+
+        /**
          * Type
          */
         public static final int Type = 1;

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties Wed Nov 28 09:03:14 2012
@@ -14,5 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-Name        = Name
-Type        = Type
+MaximumValue      = Maximum value
+MeanValue         = Mean value
+MinimumValue      = Minimum value
+Name              = Name
+NumberOfValues    = Number of values
+NumberOfNaN       = Number of \u2018NaN\u2019
+RootMeanSquare    = Root Mean Square
+StandardDeviation = Standard deviation
+Type              = Type

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties Wed Nov 28 09:03:14 2012
@@ -14,5 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-Name        = Nom
-Type        = Type
+MaximumValue      = Valeur maximale
+MeanValue         = Valeur moyenne
+MinimumValue      = Valeur minimale
+Name              = Nom
+NumberOfValues    = Nombre de valeurs
+NumberOfNaN       = Nombre de \u2018NaN\u2019
+RootMeanSquare    = Moyenne quadratique
+StandardDeviation = \u00c9cart type
+Type              = Type

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java?rev=1414596&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java Wed Nov 28 09:03:14 2012
@@ -0,0 +1,268 @@
+/*
+ * 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 java.util.Random;
+import java.util.Locale;
+import java.io.IOException;
+import org.junit.Test;
+import org.apache.sis.test.TestCase;
+
+import static java.lang.StrictMath.*;
+import static java.lang.Double.NaN;
+import static java.lang.Double.isNaN;
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link Statistics}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.00)
+ * @version 0.3
+ * @module
+ */
+public final strictfp class StatisticsTest extends TestCase {
+    /**
+     * For floating point comparisons.
+     */
+    private static final double EPS = 1E-10;
+
+    /**
+     * Tests the initial state of newly constructed instance.
+     */
+    @Test
+    public void testInitialState() {
+        final Statistics statistics = new Statistics();
+        assertEquals(0,  statistics.count());
+        assertEquals(0,  statistics.countNaN());
+        assertTrue(isNaN(statistics.minimum()));
+        assertTrue(isNaN(statistics.maximum()));
+        assertTrue(isNaN(statistics.mean()));
+        assertTrue(isNaN(statistics.rms()));
+        assertTrue(isNaN(statistics.standardDeviation(true)));
+        assertTrue(isNaN(statistics.standardDeviation(false)));
+    }
+
+    /**
+     * Tests the statistics over a large range of Gaussian values.
+     * Means should be close to 0, RMS and standard deviation should be close to 1.
+     */
+    @Test
+    public void testGaussian() {
+        final Random random = new Random(317780561);
+        final Statistics statistics = new Statistics();
+        for (int i=0; i<10000; i++) {
+            statistics.add(random.nextGaussian());
+        }
+        assertEquals(0, statistics.countNaN());
+        assertEquals(10000, statistics.count());
+        assertEquals(0, statistics.mean(), 0.01);
+        assertEquals(1, statistics.rms (), 0.01);
+        assertEquals(1, statistics.standardDeviation(false), 0.01);
+    }
+
+    /**
+     * Tests the statistics over a large range of values distributed between 0 and 1.
+     * Means should be close to 0, minimum and maximum close to -1 and +1 respectively.
+     */
+    @Test
+    public void testUniform() {
+        // Theorical values for uniform distribution.
+        final double lower  = -1;
+        final double upper  = +1;
+        final double range  = upper - lower;
+        final double stdDev = sqrt(range*range / 12);
+
+        // Now tests.
+        final Random random = new Random(309080660);
+        final Statistics statistics = new Statistics();
+        for (int i=0; i<10000; i++) {
+            statistics.add(random.nextDouble()*range + lower);
+        }
+        assertEquals(0,      statistics.countNaN());
+        assertEquals(10000,  statistics.count());
+        assertEquals(0.0,    statistics.mean(),    0.01);
+        assertEquals(lower,  statistics.minimum(), 0.01);
+        assertEquals(upper,  statistics.maximum(), 0.01);
+        assertEquals(stdDev, statistics.rms(),     0.01);
+        assertEquals(stdDev, statistics.standardDeviation(false), 0.01);
+    }
+
+    /**
+     * Same than {@link #testUniform()}, but on integer values.
+     * Used for testing {@link Statistics#add(long)}.
+     */
+    @Test
+    public void testUniformUsingIntegers() {
+        // Theorical values for uniform distribution.
+        final int    lower  = -1000;
+        final int    upper  = +1000;
+        final int    range  = upper - lower;
+        final double stdDev = sqrt(range*range / 12.0);
+
+        // Now tests.
+        final Random random = new Random(309080660);
+        final Statistics statistics = new Statistics();
+        for (int i=0; i<10000; i++) {
+            statistics.add(random.nextInt(range) + lower);
+        }
+        assertEquals(0,      statistics.countNaN());
+        assertEquals(10000,  statistics.count());
+        assertEquals(0.0,    statistics.mean(),    10.0);
+        assertEquals(lower,  statistics.minimum(), 10.0);
+        assertEquals(upper,  statistics.maximum(), 10.0);
+        assertEquals(stdDev, statistics.rms(),     10.0);
+        assertEquals(stdDev, statistics.standardDeviation(false), 10.0);
+    }
+
+    /**
+     * Tests the statistics starting with a number big enough to make the code fails if we were
+     * not using the <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan
+     * summation algorithm</a>.
+     */
+    @Test
+    public void testKahanAlgorithm() {
+        final double[] offsetAndTolerancePairs = {
+            // Starting with a reasonably small value, the result should be accurate
+            // with or without Kahan algorithm. So we use that for testing the test.
+            1000, 0.002,
+
+            // First power of 10 for which the summation
+            // fails if we don't use the Kahan algorithm.
+            1E+16, 0.003,
+
+            // Kahan algorithm still good here.
+            1E+18, 0.003,
+
+            // Last power of 10 before the summation fails
+            // using the Kahan algorithm. Quality is lower.
+            1E+19, 0.125,
+
+            // Starting with this number fails in all case using our algorithm.
+            // We test this value only in order to test our test method...
+            1E+20, 0
+        };
+        final Random random = new Random(223386491);
+        final Statistics statistics = new Statistics();
+        for (int k=0; k<offsetAndTolerancePairs.length; k++) {
+            final double offset = offsetAndTolerancePairs[k];
+            final double tolerance = offsetAndTolerancePairs[++k];
+            assertTrue("Possible misorder in offsetAndTolerancePairs", offset > 10);
+            assertTrue("Possible misorder in offsetAndTolerancePairs", tolerance < 0.2);
+            statistics.reset();
+            statistics.add(offset);
+            for (int i=0; i<10000; i++) {
+                statistics.add(random.nextDouble());
+            }
+            final double r = statistics.mean() - offset / statistics.count();
+            final double expected = (tolerance != 0) ? 0.5 : 0;
+            assertEquals(expected, r, tolerance);
+
+            statistics.add(-offset); // Accuracy will be better than in previous test.
+            assertEquals(expected, statistics.mean(), min(tolerance, 0.1));
+        }
+    }
+
+    /**
+     * Tests the concatenation of many {@link Statistics} objects.
+     */
+    @Test
+    public void testConcatenation() {
+        final Random random = new Random(429323868);
+        final Statistics global = new Statistics();
+        final Statistics byBlock = new Statistics();
+        for (int i=0; i<10; i++) {
+            final Statistics block = new Statistics();
+            for (int j=0; j<1000; j++) {
+                final double value;
+                if (random.nextInt(800) == 0) {
+                    value = Double.NaN;
+                } else {
+                    value = random.nextGaussian() + 10*random.nextDouble();
+                }
+                global.add(value);
+                block.add(value);
+            }
+            byBlock.add(block);
+            if (i == 0) {
+                assertEquals("Adding for the first time; should have the same amount of data.",   block,  byBlock);
+                assertEquals("Adding for the first time; should have got exactly the same data.", global, byBlock);
+            } else {
+                assertFalse("Should have more data that the block we just computed.",
+                        byBlock.equals(block));
+            }
+            assertEquals(global.count(),    byBlock.count());
+            assertEquals(global.countNaN(), byBlock.countNaN());
+            assertEquals(global.minimum(),  byBlock.minimum(), 0.0);
+            assertEquals(global.maximum(),  byBlock.maximum(), 0.0);
+            assertEquals(global.mean(),     byBlock.mean(),    1E-15);
+            assertEquals(global.rms(),      byBlock.rms(),     1E-15);
+        }
+    }
+
+    /**
+     * Tests the serialization.
+     *
+     * @throws IOException Should never happen.
+     */
+    @Test
+    public void testSerialization() throws IOException {
+        final Statistics statistics = new Statistics();
+        statistics.add(40);
+        statistics.add(10);
+        statistics.add(NaN);
+        statistics.add(20);
+        final Statistics after = assertSerializedEquals(statistics);
+        assertNotSame(statistics, after);
+        assertEquals( 3,                 after.count());
+        assertEquals( 1,                 after.countNaN());
+        assertEquals(10.0,               after.minimum(),                0.0);
+        assertEquals(40.0,               after.maximum(),                0.0);
+        assertEquals(23.333333333333333, after.mean(),                   EPS);
+        assertEquals(26.457513110645905, after.rms(),                    EPS);
+        assertEquals(12.472191289246473, after.standardDeviation(true),  EPS);
+        assertEquals(15.275252316519468, after.standardDeviation(false), EPS);
+    }
+
+    /**
+     * Tests the formatting of {@link Statistics}.
+     * Actually, we will instantiate the {@link StatisticsFormat} directly in order to fix the
+     * locale to a hard-coded value. But except for the localization, the result should be nearly
+     * identical to a call to the {@link Statistics#toString()} method.
+     */
+    @Test
+    public void testFormatting() {
+        final Statistics statistics = Statistics.forSeries(2);
+        statistics.add(10);
+        statistics.add(15);
+        statistics.add(22);
+        statistics.add(17);
+        statistics.add(12);
+        statistics.add( 3);
+
+        final StatisticsFormat format = new StatisticsFormat(Locale.US, null);
+        final String text = format.format(statistics);
+        assertMultilinesEquals(
+                "Number of values:       6     5      4\n" +
+                "Minimum value:       3.00 -9.00 -12.00\n" +
+                "Maximum value:      22.00  7.00   2.00\n" +
+                "Mean value:         13.17 -1.40  -3.50\n" +
+                "Root Mean Square:   14.44  6.40   6.40\n" +
+                "Standard deviation:  6.49  6.99   6.19\n", text);
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java Wed Nov 28 09:03:14 2012
@@ -43,6 +43,7 @@ import org.junit.runners.Suite;
   org.apache.sis.util.resources.IndexedResourceBundleTest.class,
   org.apache.sis.util.logging.PerformanceLevelTest.class,
   org.apache.sis.math.MathFunctionsTest.class,
+  org.apache.sis.math.StatisticsTest.class,
 
   // Collections.
   org.apache.sis.internal.util.ReferenceQueueConsumerTest.class,

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java?rev=1414596&r1=1414595&r2=1414596&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/util/resources/IndexedResourceBundleTest.java Wed Nov 28 09:03:14 2012
@@ -155,12 +155,12 @@ public final strictfp class IndexedResou
         InternationalString i18n = Errors.formatInternational(Errors.Keys.NullArgument_1);
         assertEquals("Argument ‘{0}’ shall not be null.",      i18n.toString(Locale.ENGLISH));
         assertEquals("L’argument ‘{0}’ ne doit pas être nul.", i18n.toString(Locale.FRENCH));
-        assertSerializedEquals(i18n);
+        assertNotSame(i18n, assertSerializedEquals(i18n));
 
         i18n = Errors.formatInternational(Errors.Keys.NullArgument_1, "CRS");
         assertEquals("Argument ‘CRS’ shall not be null.",      i18n.toString(Locale.ENGLISH));
         assertEquals("L’argument ‘CRS’ ne doit pas être nul.", i18n.toString(Locale.FRENCH));
-        assertSerializedEquals(i18n);
+        assertNotSame(i18n, assertSerializedEquals(i18n));
     }
 
     /**



Mime
View raw message