sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1535873 [2/2] - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/ c...
Date Fri, 25 Oct 2013 21:12:15 GMT
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
-import java.util.Collections;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -56,7 +55,7 @@ import org.apache.sis.internal.jdk7.Obje
  *   <li>Create a {@code VerticalDatum} by invoking the {@code createVerticalDatum(…)}
  *       method defined in the {@link org.opengis.referencing.datum.DatumFactory} interface.</li>
  *   <li>Create a {@code DefaultVerticalDatum} by invoking the
- *       {@linkplain #DefaultVerticalDatum(Map, Date) constructor}.</li>
+ *       {@linkplain #DefaultVerticalDatum(Map, VerticalDatumType) constructor}.</li>
  * </ol>
  *
  * <b>Example:</b> the following code gets a vertical datum for height above the geoid:
@@ -88,18 +87,6 @@ public class DefaultVerticalDatum extend
     private VerticalDatumType type;
 
     /**
-     * Creates a vertical datum from a name. This is a convenience constructor for
-     * {@link #DefaultVerticalDatum(Map, VerticalDatumType) DefaultVerticalDatum(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property.
-     *
-     * @param name The datum name.
-     * @param type The type of this vertical datum.
-     */
-    public DefaultVerticalDatum(final String name, final VerticalDatumType type) {
-        this(Collections.singletonMap(NAME_KEY, name), type);
-    }
-
-    /**
      * Creates a vertical datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      *

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -862,12 +862,13 @@ public final class Matrices extends Stat
     public static String toString(final Matrix matrix) {
         final int numRow = matrix.getNumRow();
         final int numCol = matrix.getNumCol();
-        final String[]  elements            = new String [numCol * numRow];
-        final boolean[] noFractionDigits    = new boolean[numCol * numRow];
-        final boolean[] hasDecimalSeparator = new boolean[numCol];
-        final int[] maximumFractionDigits   = new int    [numCol];
-        final int[] widthBeforeFraction     = new int    [numCol]; // spacing + ('-') + integerDigits + '.'
-        final int[] columnWidth             = new int    [numCol];
+        final String[]  elements            = new String [numCol * numRow]; // String representation of matrix values.
+        final boolean[] noFractionDigits    = new boolean[numCol * numRow]; // Whether to remove the trailing ".0" for a given number.
+        final boolean[] hasDecimalSeparator = new boolean[numCol];          // Whether the column has at least one number where fraction digits are shown.
+        final byte[] maximumFractionDigits  = new byte   [numCol];          // The greatest amount of fraction digits found in a column.
+        final byte[] maximumPaddingZeros    = new byte   [numCol * numRow]; // Maximal amount of zeros that we can append before to exceed the IEEE 754 accuracy.
+        final byte[] widthBeforeFraction    = new byte   [numCol];          // Number of characters before the fraction digits: spacing + ('-') + integerDigits + '.'
+        final byte[] columnWidth            = new byte   [numCol];          // Total column width.
         int totalWidth = 1;
         /*
          * Create now the string representation of all matrix elements and measure the width
@@ -888,26 +889,39 @@ public final class Matrices extends Stat
                 if (value == -1 || value == 0 || value == +1) {
                     noFractionDigits[flatIndex] = true;
                     width = spacing + element.length() - 2; // The -2 is for ignoring the trailing ".0"
-                    widthBeforeFraction[i] = Math.max(widthBeforeFraction[i], width);
+                    widthBeforeFraction[i] = (byte) Math.max(widthBeforeFraction[i], width);
                 } else {
                     /*
-                     * All values other than ±0 and ±1. Store separately the width before and after
-                     * the decimal separator. The width before the separator contains the spacing.
+                     * All values other than ±0 and ±1. If the values is NaN or infinity (in which case there is
+                     * no decimal separator), give all spaces to the "before fraction" side for right-alignment.
                      */
                     int s = element.lastIndexOf('.');
-                    if (s >= 0) {
-                        s++; // After the separator.
-                        hasDecimalSeparator[i] = true;
-                        width = (widthBeforeFraction  [i] = Math.max(widthBeforeFraction  [i], spacing + s))
-                              + (maximumFractionDigits[i] = Math.max(maximumFractionDigits[i], element.length() - s));
-                    } else {
-                        // NaN or Infinity.
+                    if (s < 0) {
                         element = element.replace("Infinity", "∞");
                         width = spacing + element.length();
-                        widthBeforeFraction[i] = Math.max(widthBeforeFraction[i], width);
+                        widthBeforeFraction[i] = (byte) Math.max(widthBeforeFraction[i], width);
+                    } else {
+                        /*
+                         * All values other than ±0, ±1, NaN and infinity. We store separately the width before
+                         * and after the decimal separator. The width before the separator contains the spacing
+                         * between cells.
+                         */
+                        hasDecimalSeparator[i] = true;
+                        final int numFractionDigits = element.length() - ++s;
+                        width = (widthBeforeFraction  [i] = (byte) Math.max(widthBeforeFraction  [i], spacing + s))
+                              + (maximumFractionDigits[i] = (byte) Math.max(maximumFractionDigits[i], numFractionDigits));
+                        /*
+                         * If the number use exponential notation, we will not be allowed to append any zero.
+                         * Otherwise we will append some zeros for right-alignment, but without exceeding the
+                         * IEEE 754 'double' accuracy for not giving a false sense of precision.
+                         */
+                        if (element.indexOf('E') < 0) {
+                            final int accuracy = (int) Math.ceil(-Math.log10(Math.ulp(value)));
+                            maximumPaddingZeros[flatIndex] = (byte) (accuracy - numFractionDigits);
+                        }
                     }
                 }
-                columnWidth[i] = Math.max(columnWidth[i], width);
+                columnWidth[i] = (byte) Math.max(columnWidth[i], width);
                 elements[flatIndex] = element;
             }
             totalWidth += columnWidth[i];
@@ -935,18 +949,23 @@ public final class Matrices extends Stat
                     spaces = columnWidth[i] - width; // Number of spaces for right alignment (NaN or ∞ cases)
                 }
                 buffer.append(CharSequences.spaces(spaces)).append(element);
-                /*
-                 * Append trailing spaces for ±0 and ±1 values,
-                 * or trailing zeros for all other real values.
-                 */
-                s += maximumFractionDigits[i] - width;
-                if (noFractionDigits[flatIndex]) {
-                    buffer.setLength(buffer.length() - 2); // Erase the trailing ".0"
-                    buffer.append(CharSequences.spaces(s + 2));
-                } else {
-                    while (--s >= 0) {
-                        buffer.append('0');
+                if (s >= 0) {
+                    /*
+                     * Append trailing spaces for ±0 and ±1 values,
+                     * or trailing zeros for all other real values.
+                     */
+                    s += maximumFractionDigits[i] - width;
+                    if (noFractionDigits[flatIndex]) {
+                        buffer.setLength(buffer.length() - 2); // Erase the trailing ".0"
+                        s += 2;
+                    } else {
+                        int n = Math.min(s, maximumPaddingZeros[flatIndex]);
+                        s -= n;
+                        while (--n >= 0) {
+                            buffer.append('0');
+                        }
                     }
+                    buffer.append(CharSequences.spaces(s));
                 }
                 flatIndex++;
             }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -18,6 +18,7 @@ package org.apache.sis.geometry;
 
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
+import org.apache.sis.math.MathFunctions;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOnMethod;
@@ -27,7 +28,6 @@ import org.junit.Test;
 import static java.lang.Double.NaN;
 import static org.opengis.test.Validators.*;
 import static org.apache.sis.referencing.Assert.*;
-import static org.apache.sis.math.MathFunctions.isNegative;
 import static org.apache.sis.geometry.AbstractEnvelopeTest.WGS84;
 
 
@@ -95,7 +95,7 @@ public strictfp class GeneralEnvelopeTes
             final double xLower, final double ymin, final double xUpper, final double ymax)
     {
         final double xmin, xmax;
-        if (isNegative(xUpper - xLower)) { // Check for anti-meridian spanning.
+        if (MathFunctions.isNegative(xUpper - xLower)) { // Check for anti-meridian spanning.
             xmin = -180;
             xmax = +180;
         } else {
@@ -395,8 +395,8 @@ public strictfp class GeneralEnvelopeTes
 
         e = create(0, 10, 360, 20);
         assertTrue(e.normalize());
-        assertEquals("Expect positive zero", Double.doubleToLongBits(+0.0), Double.doubleToLongBits(e.getLower(0)));
-        assertEquals("Expect negative zero", Double.doubleToLongBits(-0.0), Double.doubleToLongBits(e.getUpper(0)));
+        assertTrue("Expect positive zero", MathFunctions.isPositiveZero(e.getLower(0)));
+        assertTrue("Expect negative zero", MathFunctions.isNegativeZero(e.getUpper(0)));
         verifyInvariants(e);
     }
 

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -348,19 +348,23 @@ public final strictfp class MatricesTest
                 "└               ┘\n", new Matrix3(1, 0, 0, NaN, NaN, NaN, 0, 0, 1).toString());
         /*
          * Mix of values with different precision, ±0, ±1, NaN and infinities.
+         * In addition, the first column contains numbers having the maximal number of digits allowed
+         * by the IEEE 754 'double' representation (we put an additional trailing '1' for making sure
+         * that we exceed the 'double' accuracy). Our string representation shall put spaces, not 0,
+         * for those numbers in order to not give a false sense of accuracy.
          */
         final MatrixSIS matrix = Matrices.create(4, 5, new double[] {
-            39.5193682106975150,  -68.5200,  -1.0,  1,  98,
-           -66.0358637477182200,       NaN,  43.0,  0,  Double.NEGATIVE_INFINITY,
+            39.5193682106975151,  -68.5200,  -1.0,  1,  98,
+           -66.0358637477182201,       NaN,  43.0,  0,  Double.NEGATIVE_INFINITY,
              2.0741018968776337,   83.7260,  -0.0,  1,  -3,
-            91.8796187759200600,  -18.2674,  24.5,  0,  36.5
+            91.8796187759200601,  -18.2674,  24.5,  0,  36.5
         });
         assertMultilinesEquals(
                 "┌                                               ┐\n" +
-                "│  39.5193682106975150  -68.5200  -1    1  98.0 │\n" +
-                "│ -66.0358637477182200       NaN  43.0  0    -∞ │\n" +
+                "│  39.519368210697515   -68.5200  -1    1  98.0 │\n" +
+                "│ -66.03586374771822         NaN  43.0  0    -∞ │\n" +
                 "│   2.0741018968776337   83.7260  -0    1  -3.0 │\n" +
-                "│  91.8796187759200600  -18.2674  24.5  0  36.5 │\n" +
+                "│  91.87961877592006    -18.2674  24.5  0  36.5 │\n" +
                 "└                                               ┘\n", matrix.toString());
     }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -98,6 +98,6 @@ public final strictfp class Matrix3Test 
                 0,  0,  1}, expected, TOLERANCE);
 
         // Now the actual test.
-        assertMatrixEquals(expected, SIZE, SIZE, A.solve(B), TOLERANCE);
+        assertEqualsElements(expected, SIZE, SIZE, A.solve(B), TOLERANCE);
     }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -160,7 +160,7 @@ public abstract strictfp class MatrixTes
      * @param actual    The SIS matrix to compare to JAMA.
      * @param tolerance The tolerance threshold, usually either {@link #STRICT} or {@link #TOLERANCE}.
      */
-    static void assertMatrixEquals(final Matrix expected, final MatrixSIS actual, final double tolerance) {
+    static void assertEqualsJAMA(final Matrix expected, final MatrixSIS actual, final double tolerance) {
         final int numRow = actual.getNumRow();
         final int numCol = actual.getNumCol();
         assertEquals("numRow", expected.getRowDimension(),    numRow);
@@ -184,7 +184,7 @@ public abstract strictfp class MatrixTes
      * Asserts that the given matrix is equals to the given expected values, up to the given tolerance threshold.
      * This method compares the elements values in two slightly redundant ways.
      */
-    static void assertMatrixEquals(final double[] expected, final int numRow, final int numCol,
+    static void assertEqualsElements(final double[] expected, final int numRow, final int numCol,
             final MatrixSIS actual, final double tolerance)
     {
         assertEquals("numRow", numRow, actual.getNumRow());
@@ -227,7 +227,7 @@ public abstract strictfp class MatrixTes
          * The JAMA constructor uses column-major array (FORTRAN convention), while SIS uses
          * row-major array. So we have to transpose the JAMA matrix after construction.
          */
-        assertMatrixEquals(new Matrix(elements, numCol).transpose(), matrix, STRICT);
+        assertEqualsJAMA(new Matrix(elements, numCol).transpose(), matrix, STRICT);
         assertArrayEquals("getElements", elements, matrix.getElements(), STRICT);
     }
 
@@ -249,14 +249,14 @@ public abstract strictfp class MatrixTes
         /*
          * End of initialization - now perform the actual test.
          */
-        assertMatrixEquals(reference, matrix, STRICT);
+        assertEqualsJAMA(reference, matrix, STRICT);
         for (int k=0; k<50; k++) {
             final int    j = random.nextInt(numRow);
             final int    i = random.nextInt(numCol);
             final double e = random.nextDouble() * 100;
             reference.set(j, i, e);
             matrix.setElement(j, i, e);
-            assertMatrixEquals(reference, matrix, STRICT);
+            assertEqualsJAMA(reference, matrix, STRICT);
         }
     }
 
@@ -343,7 +343,7 @@ public abstract strictfp class MatrixTes
          * array. In other words, the JAMA matrix is already transposed from the SIS point of view.
          */
         matrix.transpose();
-        assertMatrixEquals(new Matrix(elements, numCol), matrix, STRICT);
+        assertEqualsJAMA(new Matrix(elements, numCol), matrix, STRICT);
     }
 
     /**
@@ -402,7 +402,7 @@ public abstract strictfp class MatrixTes
              */
             final Matrix referenceResult = reference.times(referenceArg);
             final MatrixSIS matrixResult = matrix.multiply(matrixArg);
-            assertMatrixEquals(referenceResult, matrixResult, TOLERANCE);
+            assertEqualsJAMA(referenceResult, matrixResult, TOLERANCE);
         }
     }
 
@@ -442,7 +442,7 @@ public abstract strictfp class MatrixTes
              */
             final Matrix referenceResult = reference.solve(referenceArg);
             final MatrixSIS matrixResult = matrix.solve(matrixArg);
-            assertMatrixEquals(referenceResult, matrixResult, SolverTest.TOLERANCE);
+            assertEqualsJAMA(referenceResult, matrixResult, SolverTest.TOLERANCE);
         }
     }
 
@@ -466,7 +466,7 @@ public abstract strictfp class MatrixTes
                 continue; // To close to a singular matrix - search an other one.
             }
             final MatrixSIS matrix = Matrices.create(numRow, numCol, elements);
-            assertMatrixEquals(reference.inverse(), matrix.inverse(), TOLERANCE);
+            assertEqualsJAMA(reference.inverse(), matrix.inverse(), TOLERANCE);
         }
     }
 

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -124,7 +124,7 @@ public final strictfp class NonSquareMat
             0,       0,            1
         };
         final MatrixSIS inverse = (Y != null) ? matrix.solve(Y) : matrix.inverse();
-        assertMatrixEquals(expected, 5, 3, inverse, TOLERANCE);
+        assertEqualsElements(expected, 5, 3, inverse, TOLERANCE);
     }
 
     /**
@@ -151,7 +151,7 @@ public final strictfp class NonSquareMat
             0,       0,  0,        0,   1
         };
         final MatrixSIS inverse = (Y != null) ? matrix.solve(Y) : matrix.inverse();
-        assertMatrixEquals(expected, 3, 5, inverse, TOLERANCE);
+        assertEqualsElements(expected, 3, 5, inverse, TOLERANCE);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -25,7 +25,8 @@ import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static java.lang.Double.NaN;
-import static org.apache.sis.referencing.operation.matrix.MatrixTestCase.assertMatrixEquals;
+import static org.apache.sis.referencing.operation.matrix.MatrixTestCase.assertEqualsJAMA;
+import static org.apache.sis.referencing.operation.matrix.MatrixTestCase.assertEqualsElements;
 
 
 /**
@@ -114,7 +115,7 @@ public final strictfp class SolverTest e
                 continue;
             }
             final MatrixSIS U = Solver.solve(matrix, matrixArg);
-            assertMatrixEquals(jama, U, TOLERANCE);
+            assertEqualsJAMA(jama, U, TOLERANCE);
         }
     }
 
@@ -144,7 +145,7 @@ public final strictfp class SolverTest e
             0,     0,  0,      0,    1
         };
         MatrixSIS inverse = Solver.inverse(matrix, false);
-        assertMatrixEquals(expected, 5, 5, inverse, TOLERANCE);
+        assertEqualsElements(expected, 5, 5, inverse, TOLERANCE);
         /*
          * Set a scale factor to NaN. The translation term for the corresponding
          * dimension become unknown, so it most become NaN in the inverse matrix.
@@ -164,7 +165,7 @@ public final strictfp class SolverTest e
             0,     0,  0,      0,    1
         };
         inverse = Solver.inverse(matrix, false);
-        assertMatrixEquals(expected, 5, 5, inverse, TOLERANCE);
+        assertEqualsElements(expected, 5, 5, inverse, TOLERANCE);
         /*
          * Set a scale factor to NaN with translation equals to 0.
          * The zero value should be preserved, since 0 × any == 0
@@ -185,7 +186,7 @@ public final strictfp class SolverTest e
             0,     0,  0,      0,    1
         };
         inverse = Solver.inverse(matrix, false);
-        assertMatrixEquals(expected, 5, 5, inverse, TOLERANCE);
+        assertEqualsElements(expected, 5, 5, inverse, TOLERANCE);
         /*
          * Set a translation term to NaN. The translation should be NaN in
          * the inverse matrix too, but the scale factor can still be compute.
@@ -205,6 +206,6 @@ public final strictfp class SolverTest e
             0,     0,  0,      0,    1
         };
         inverse = Solver.inverse(matrix, false);
-        assertMatrixEquals(expected, 5, 5, inverse, TOLERANCE);
+        assertEqualsElements(expected, 5, 5, inverse, TOLERANCE);
     }
 }

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -47,6 +47,7 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.NamedIdentifierTest.class,
     org.apache.sis.referencing.AbstractIdentifiedObjectTest.class,
     org.apache.sis.referencing.AbstractReferenceSystemTest.class,
+    org.apache.sis.referencing.datum.BursaWolfParametersTest.class,
 
     org.apache.sis.geometry.AbstractDirectPositionTest.class,
     org.apache.sis.geometry.GeneralDirectPositionTest.class,

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -32,7 +32,7 @@ import org.apache.sis.util.CharSequences
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class JDK8 {
@@ -50,6 +50,18 @@ public final class JDK8 {
     }
 
     /**
+     * Returns the floating-point value adjacent to {@code value} in the direction of negative infinity.
+     *
+     * @param  value The value for which to get the adjacent value.
+     * @return The adjacent value in the direction of negative infinity.
+     *
+     * @since 0.4
+     */
+    public static double nextDown(final double value) {
+        return Math.nextAfter(value, Double.NEGATIVE_INFINITY);
+    }
+
+    /**
      * Parses a date from a string in ISO 8601 format. More specifically, this method expects the
      * format defined by <cite>XML Schema Part 2: Datatypes for {@code xsd:dateTime}</cite>, with
      * some additional flexibility (e.g. missing minutes or seconds fields are automatically added).

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -176,6 +176,7 @@ public final class CollectionsExt extend
     public static <E> E[] nonNullArraySet(final String name, final Object value, final E[] emptyArray)
             throws IllegalArgumentException
     {
+        assert emptyArray.length == 0;
         if (value == null) {
             return emptyArray;
         }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -85,10 +85,10 @@ public final class DoubleDouble extends 
 
     /**
      * When computing <var>a</var> - <var>b</var> as a double-double (106 significand bits) value,
-     * if the amount of non-zero significand bits is equals or lower than that amount, consider the
-     * result as zero.
+     * if the amount of non-zero significand bits is equals or lower than {@code ZERO_THRESHOLD+1},
+     * consider the result as zero.
      */
-    private static final int ZERO_THRESHOLD = 3;
+    private static final int ZERO_THRESHOLD = 2;
 
     /**
      * The split constant used as part of multiplication algorithms. The split algorithm is as below
@@ -144,6 +144,7 @@ public final class DoubleDouble extends 
          0.9,                                       // Degrees to gradians
          0.9144,                                    // Yard to metres
          1.111111111111111111111111111111111,       // Gradian to degrees
+         1.414213562373095048801688724209698,       // √2
          1.570796326794896619231321691639751,       // π/2
          1.8288,                                    // Fathom to metres
          2.356194490192344928846982537459627,       // π * 3/4
@@ -183,6 +184,7 @@ public final class DoubleDouble extends 
         /*  0.9       */ -2.2204460492503132E-17,
         /*  0.9144    */  9.414691248821328E-18,
         /*  1.111111… */ -4.9343245538895844E-17,
+        /*  1.414213… */ -9.667293313452913E-17,
         /*  1.570796… */  6.123233995736766E-17,
         /*  1.8288    */  1.8829382497642655E-17,
         /*  2.356194… */  9.184850993605148E-17,
@@ -210,7 +212,30 @@ public final class DoubleDouble extends 
     public DoubleDouble() {
     }
 
-    /** Returns {@link #value}. */
+    /**
+     * Creates a new value initialized to the given value and error.
+     * It is caller's responsibility to ensure that the (value, error) pair is normalized.
+     *
+     * @param value The initial value.
+     * @param error The initial error.
+     */
+    public DoubleDouble(final double value, final double error) {
+        this.value = value;
+        this.error = error;
+        assert !(Math.abs(error) >= Math.ulp(value)) : this; // Use ! for being tolerant to NaN.
+    }
+
+    /**
+     * Returns a new {@code DoubleDouble} instance initialized to the conversion factor
+     * from angular degrees to radians.
+     *
+     * @return An instance initialized to the 0.01745329251994329576923690768488613 value.
+     */
+    public static DoubleDouble createDegreesToRadians() {
+        return new DoubleDouble(0.01745329251994329576923690768488613, 2.9486522708701687E-19);
+    }
+
+    /** @return {@link #value}. */
     @Override public double doubleValue() {return value;}
     @Override public float  floatValue()  {return (float) value;}
     @Override public long   longValue()   {return Math.round(value);}
@@ -434,7 +459,7 @@ public final class DoubleDouble extends 
              * The number of significand bits (mantissa) in the IEEE 'double' representation is 52,
              * not counting the hidden bit. So estimate the accuracy of the double-double number as
              * the accuracy of the 'double' value (which is 1 ULP) scaled as if we had 52 additional
-             * significand bits (we ignore some more bits if ZERO_THRESHOLD is greater than 1).
+             * significand bits (we ignore some more bits if ZERO_THRESHOLD is greater than 0).
              * If the error is not greater than that value, then assume that it is not significant.
              */
             if (Math.abs(error) <= Math.scalb(Math.ulp(otherValue), ZERO_THRESHOLD - Numerics.SIGNIFICAND_SIZE)) {
@@ -698,11 +723,33 @@ public final class DoubleDouble extends 
     /**
      * Sets this double-double value to its square root.
      *
-     * @todo This method is not yet implemented with double-double precision.
+     * {@section Implementation}
+     * This method searches for a {@code (r + ε)} value where:
+     *
+     * <blockquote>(r + ε)²  =  {@linkplain #value} + {@linkplain #error}</blockquote>
+     *
+     * If we could compute {@code r = sqrt(value + error)} with enough precision, then ε would be 0.
+     * But with the {@code double} type, we can only estimate {@code r ≈ sqrt(value)}. However, since
+     * that <var>r</var> value should be close to the "true" value, then ε should be small.
+     *
+     * <blockquote>value + error  =  (r + ε)²  =  r² + 2rε + ε²</blockquote>
+     *
+     * Neglecting ε² on the assumption that |ε| ≪ |r|:
+     *
+     * <blockquote>value + error  ≈  r² + 2rε</blockquote>
+     *
+     * Isolating ε:
+     *
+     * <blockquote>ε  ≈  (value + error - r²) / (2r)</blockquote>
      */
     public void sqrt() {
-        value = Math.sqrt(value);
-        error = 0;
+        final double thisValue = this.value;
+        final double thisError = this.error;
+        double r = Math.sqrt(thisValue);
+        setToProduct(r, r);
+        subtract(thisValue, thisError);
+        divide(-2*r, 0); // Multiplication by 2 does not cause any precision lost.
+        setToQuickSum(r, value);
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -78,9 +78,9 @@ public final class Numerics extends Stat
 
     /**
      * Number of bits in the significand (mantissa) part of IEEE 754 {@code double} representation,
-     * including the hidden bit.
+     * <strong>not</strong> including the hidden bit.
      */
-    static final int SIGNIFICAND_SIZE = 53;
+    public static final int SIGNIFICAND_SIZE = 52;
 
     /**
      * A prime number used for hash code computation. Value 31 is often used because

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -22,13 +22,14 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.DoubleDouble;
 
 import static java.lang.Float.intBitsToFloat;
 import static java.lang.Float.floatToRawIntBits;
 import static java.lang.Double.longBitsToDouble;
 import static java.lang.Double.doubleToRawLongBits;
-import org.apache.sis.internal.util.DoubleDouble;
 import static org.apache.sis.internal.util.Numerics.SIGN_BIT_MASK;
+import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
 
 
 /**
@@ -64,13 +65,33 @@ import static org.apache.sis.internal.ut
  */
 public final class MathFunctions extends Static {
     /**
-     * The square root of 2, which is {@value}.
+     * The square root of 2, which is approximated by {@value}.
      *
      * @see Math#sqrt(double)
      */
     public static final double SQRT_2 = 1.4142135623730951;
 
     /**
+     * The logarithm of 2 in base 10, which is approximated by {@value}.
+     *
+     * @see Math#log10(double)
+     * @see #getExponent(double)
+     *
+     * @since 0.4
+     */
+    public static final double LOG10_2 = 0.3010299956639812;
+
+    /**
+     * The greatest power of 10 such as {@code Math.pow(10, E10_FOR_ZERO) == 0}.
+     * This is the exponent in {@code parseDouble("1E-324")} &lt; {@link Double#MIN_VALUE},
+     * which is stored as zero because non-representable as a {@code double} value.
+     * The next power, {@code parseDouble("1E-323")}, is a non-zero {@code double} value.
+     *
+     * @see Double#MIN_VALUE
+     */
+    static final int E10_FOR_ZERO = -324;
+
+    /**
      * Table of some integer powers of 10. Used for faster computation in {@link #pow10(int)}.
      *
      * @see #pow10(int)
@@ -219,19 +240,29 @@ public final class MathFunctions extends
      *
      * <p>Special cases:</p>
      * <ul>
-     *   <li>If {@code accuracy} is 0, {@link Double#NaN NaN} or infinity,
-     *       then this method returns 0.</li>
+     *   <li>If {@code accuracy} is {@link Double#NaN NaN} or infinity, then this method returns 0
+     *       since those values are not represented by decimal digits.</li>
+     *
+     *   <li>If {@code accuracy} is 0, then this method returns 324 since 10<sup>-324</sup> is the first power of 10
+     *       smaller than the minimal strictly positive {@code double} value ({@value java.lang.Double#MIN_VALUE}).
+     *
+     *       {@note The above value can be understood in an other way: if the first 324 fraction digits are zero,
+     *              then the IEEE <code>double</code> value is guaranteed to be rounded to exactly 0 no matter
+     *              what the next fraction digits are.}</li>
+     *
      *   <li>If {@code accuracy} is greater than 1, then this method returns
      *       the number of "unnecessary" trailing zeros as a negative number.
-     *       For example {@code fractionDigitsForDelta(100, …)} returns -2.</li>
+     *
+     *       {@example <code>fractionDigitsForDelta(100, …)</code> returns -2.}</li>
+     *
      *   <li>If the first non-zero digits of {@code accuracy} are equal or greater than 95
      *       (e.g. 0.00099) and the {@code strict} argument is {@code true}, then this method
      *       increases the number of needed fraction digits in order to prevent the rounded
      *       number to be collapsed into the next integer value.
      *
      *       {@example
-     *       If {@code accuracy} is 0.95, then a return value of 1 is not sufficient since the
-     *       rounded value of 0.95 with 1 fraction digit would be 1.0. Such value would be a
+     *       If <code>accuracy</code> is 0.95, then a return value of 1 is not sufficient since
+     *       the rounded value of 0.95 with 1 fraction digit would be 1.0. Such value would be a
      *       violation of this method contract since the difference between 0 and that formatted
      *       value would be greater than the accuracy. Note that this is not an artificial rule;
      *       this is related to the fact that 0.9999… is mathematically strictly equals to 1.}</li>
@@ -274,20 +305,124 @@ public final class MathFunctions extends
         } else { // 'x' is out of range or NaN.
             final double y = Math.log10(accuracy);
             if (Double.isInfinite(y)) {
-                return 0;
+                return (accuracy == 0) ? -E10_FOR_ZERO : 0;
             }
             i = -((int) Math.floor(y));
             scale = pow10(i);
         }
-        while ((accuracy *= scale) >= 9.5) {
-            i++; // The 0.…95 special case.
-            accuracy -= Math.floor(accuracy);
-            scale = 10;
+        if (strict) {
+            while ((accuracy *= scale) >= 9.5) {
+                i++; // The 0.…95 special case.
+                accuracy -= Math.floor(accuracy);
+                scale = 10;
+            }
         }
         return i;
     }
 
     /**
+     * Returns the number of significant fraction digits when formatting the given number in base 10.
+     * This method does <strong>not</strong> ignore trailing zeros.
+     * For example {@code fractionDigitsForValue(1.0)} returns 16,
+     * because the {@code double} format can store <i>almost</i> 16 decimal digits after 1.
+     *
+     * {@note We said <i>almost</i> because the very last digit may be able to store only a subset of the
+     *        [0 … 9] digits.}
+     *
+     * Invoking this method is equivalent to invoking <code>{@linkplain #fractionDigitsForDelta(double, boolean)
+     * fractionDigitsForDelta}(Math.{@linkplain Math#ulp(double) ulp}(value), false)</code>, except that it is
+     * potentially faster.
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>If {@code value} is {@link Double#NaN NaN} or infinity, then this method returns 0
+     *       since those values are not represented by decimal digits.</li>
+     *
+     *   <li>If {@code value} is 0, then this method returns 324 since
+     *       {@code Math.ulp(0)} = {@value java.lang.Double#MIN_VALUE}.</li>
+     * </ul>
+     *
+     * {@example This method is useful with <code>NumberFormat</code> for formatting all significant digits
+     *           of a <code>double</code> value, padding with trailing zeros if necessary, but no more than
+     *           necessary.}
+     *
+     * @param  value The value for which to get the number of significant digits.
+     * @return The number of significant digits (may be negative), or 0 if {@code value} is NaN or infinity.
+     *
+     * @see java.text.NumberFormat#setMinimumFractionDigits(int)
+     *
+     * @since 0.4
+     */
+    public static int fractionDigitsForValue(final double value) {
+        /*
+         * We really need Math.getExponent(value) here rather than MathFunctions.getExponent(value).
+         * What we actually want is MathFunctions.getExponent(Math.ulp(value)), but we get the same
+         * result more efficiently if we replace the call to Math.ulp(double) by a SIGNIFICAND_SIZE
+         * subtraction in the exponent, provided that the exponent has NOT been corrected for sub-
+         * normal numbers (in order to reproduce the Math.ulp behavior).
+         */
+        final int exponent = Math.getExponent(value);
+        if (exponent <= Double.MAX_EXPONENT) { // Exclude NaN and ±∞ cases.
+            return (int) -Math.floor(LOG10_2 * (exponent - SIGNIFICAND_SIZE));
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the unbiased exponent used in the representation of a {@code double}, with correction for
+     * sub-normal numbers. This method is related to {@link Math#getExponent(double)} in the following ways:
+     *
+     * <ul>
+     *   <li>For NaN and all values equal or greater than {@link Double#MIN_NORMAL} in magnitude (including
+     *       infinities), this method returns results that are identical to {@code Math.getExponent(double)}.</li>
+     *   <li>For values smaller than {@link Double#MIN_NORMAL} in magnitude (including zero), the correction
+     *       for sub-normal numbers results in return values smaller than what {@code Math.getExponent(double)}
+     *       would return.</li>
+     * </ul>
+     *
+     * Special cases:
+     * <ul>
+     *   <li>If the argument is NaN or infinite, then the result is {@link Double#MAX_EXPONENT} + 1.</li>
+     *   <li>If the argument is {@link Double#MAX_VALUE},  then the result is {@value java.lang.Double#MAX_EXPONENT}.</li>
+     *   <li>If the argument is {@link Double#MIN_NORMAL}, then the result is {@value java.lang.Double#MIN_EXPONENT}.</li>
+     *   <li>If the argument is {@link Double#MIN_VALUE},  then the result is -1074.</li>
+     *   <li>If the argument is zero, then the result is -1075.</li>
+     * </ul>
+     *
+     * {@section Identities}
+     * For any <var>p</var> values in the [-1075 … 1024] range and <var>value</var> = 2<sup>p</sup>:
+     * <ul>
+     *   <li><code>getExponent(Math.scalb(1.0, p)) == p</code></li>
+     *   <li><code>Math.scalb(1.0, getExponent(value)) == value</code></li>
+     *   <li><code>Math.floor({@linkplain #LOG10_2} * getExponent(value)) == Math.floor(Math.log10(value))</code></li>
+     * </ul>
+     *
+     * @param  value The value for which to get the exponent.
+     * @return The unbiased exponent, corrected for sub-normal numbers if needed.
+     *         Values will be in the [-1075 … 1024] range, inclusive.
+     *
+     * @see Math#getExponent(double)
+     * @see Math#scalb(double, int)
+     *
+     * @since 0.4
+     */
+    public static int getExponent(final double value) {
+        final long bits = doubleToRawLongBits(value);
+        int exponent = (int) ((bits >>> SIGNIFICAND_SIZE) & 0x7FFL);
+        if (exponent == 0) {
+            /*
+             * Number is sub-normal: there is no implicit 1 bit before the significand.
+             * We need to search for the position of the first real 1 bit, and fix the
+             * exponent accordingly.  Note that numberOfLeadingZeros(…) is relative to
+             * 64 bits while the significand size is only 52 bits. The last term below
+             * is for fixing this difference.
+             */
+            exponent -= Long.numberOfLeadingZeros(bits & ((1L << SIGNIFICAND_SIZE) - 1)) - (Long.SIZE - SIGNIFICAND_SIZE);
+        }
+        return exponent - Double.MAX_EXPONENT;
+    }
+
+    /**
      * Computes 10 raised to the power of <var>x</var>. This method delegates to
      * <code>{@linkplain #pow10(int) pow10}((int) x)</code> if <var>x</var> is an
      * integer, or to <code>{@linkplain Math#pow(double, double) Math.pow}(10, x)</code>
@@ -388,12 +523,36 @@ public final class MathFunctions extends
      *
      * @param  value The value to test.
      * @return {@code true} if the given value is positive, excluding negative zero.
+     *
+     * @see #isPositiveZero(double)
+     * @see #isNegative(double)
      */
     public static boolean isPositive(final double value) {
         return (doubleToRawLongBits(value) & SIGN_BIT_MASK) == 0 && !Double.isNaN(value);
     }
 
     /**
+     * Returns {@code true} if the given value is the positive zero ({@code +0.0}).
+     * This method returns {@code false} for the negative zero ({@code -0.0}).
+     * This method is equivalent to the following code, but potentially faster:
+     *
+     * {@preformat java
+     *   return (value == 0) && isPositive(value);
+     * }
+     *
+     * @param  value The value to test.
+     * @return {@code true} if the given value is +0.0 (not -0.0).
+     *
+     * @see #isPositive(double)
+     * @see #isNegativeZero(double)
+     *
+     * @since 0.4
+     */
+    public static boolean isPositiveZero(final double value) {
+        return doubleToRawLongBits(value) == 0L;
+    }
+
+    /**
      * Returns {@code true} if the given value is negative, <em>including</em> negative zero.
      * Special cases:
      *
@@ -409,12 +568,36 @@ public final class MathFunctions extends
      *
      * @param  value The value to test.
      * @return {@code true} if the given value is negative, including negative zero.
+     *
+     * @see #isNegativeZero(double)
+     * @see #isPositive(double)
      */
     public static boolean isNegative(final double value) {
         return (doubleToRawLongBits(value) & SIGN_BIT_MASK) != 0 && !Double.isNaN(value);
     }
 
     /**
+     * Returns {@code true} if the given value is the negative zero ({@code -0.0}).
+     * This method returns {@code false} for the positive zero ({@code +0.0}).
+     * This method is equivalent to the following code, but potentially faster:
+     *
+     * {@preformat java
+     *   return (value == 0) && isNegative(value);
+     * }
+     *
+     * @param  value The value to test.
+     * @return {@code true} if the given value is -0.0 (not +0.0).
+     *
+     * @see #isNegative(double)
+     * @see #isPositiveZero(double)
+     *
+     * @since 0.4
+     */
+    public static boolean isNegativeZero(final double value) {
+        return doubleToRawLongBits(value) == SIGN_BIT_MASK;
+    }
+
+    /**
      * Returns {@code true} if the given values have the same sign, differentiating positive
      * and negative zeros.
      * Special cases:
@@ -529,9 +712,9 @@ public final class MathFunctions extends
      * @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
      */
     public static short sgn(short x) {
-        if (x > 0) return (short) +1;
-        if (x < 0) return (short) -1;
-        else       return (short)  0;
+        if (x > 0) return +1;
+        if (x < 0) return -1;
+        else       return  0;
     }
 
     /**
@@ -544,9 +727,9 @@ public final class MathFunctions extends
      * @return {@code +1} if <var>x</var> is positive, {@code -1} if negative, or 0 otherwise.
      */
     public static byte sgn(byte x) {
-        if (x > 0) return (byte) +1;
-        if (x < 0) return (byte) -1;
-        else       return (byte)  0;
+        if (x > 0) return +1;
+        if (x < 0) return -1;
+        else       return  0;
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -895,7 +895,7 @@ public class AngleFormat extends Format 
      *
      * @return The {@code toAppendTo} buffer, returned for method calls chaining.
      */
-    public StringBuffer format(double angle, StringBuffer toAppendTo, final FieldPosition pos) {
+    public StringBuffer format(final double angle, StringBuffer toAppendTo, final FieldPosition pos) {
         final int offset = toAppendTo.length();
         final int fieldPos = getField(pos);
         if (isNaN(angle) || isInfinite(angle)) {
@@ -913,9 +913,18 @@ public class AngleFormat extends Format 
         double degrees = angle;
         double minutes = NaN;
         double seconds = NaN;
+        int maximumFractionDigits = fractionFieldWidth;
         if (minutesFieldWidth != 0 && !isNaN(angle)) {
             minutes = abs(degrees - (degrees = truncate(degrees))) * 60;
-            final double p = pow10(fractionFieldWidth);
+            /*
+             * Limit the maximal number of fraction digits to the amount of significant digits for a 'double' value.
+             * The intend is to avoid non-significant garbage that are pure artifacts from the conversion from base
+             * 2 to base 10.
+             */
+            final int n = fractionDigitsForDelta(Math.ulp(angle) * (secondsFieldWidth == 0 ? 60 : 3600), false);
+            maximumFractionDigits = Math.max(minimumFractionDigits,
+                                    Math.min(maximumFractionDigits, n - 1));
+            final double p = pow10(maximumFractionDigits);
             if (secondsFieldWidth != 0) {
                 seconds = (minutes - (minutes = truncate(minutes))) * 60;
                 seconds = rint(seconds * p) / p; // Correction for rounding errors.
@@ -946,22 +955,7 @@ public class AngleFormat extends Format 
             }
         }
         /*
-         * At this point the 'degrees', 'minutes' and 'seconds' variables contain the final values
-         * to format. But before to perform the numbers formating,  if the pattern uses a variable
-         * number of fraction digits, then limit the maximal number to the amount of significant
-         * fraction digits for a 'double' value. The intend is to avoid non-significant garbage
-         * that are pure artifacts from the conversion from base 2 to base 10.
-         */
-        int maximumFractionDigits = fractionFieldWidth;
-        if (maximumFractionDigits != minimumFractionDigits) {
-            if      (secondsFieldWidth != 0) angle *= 3600;
-            else if (minutesFieldWidth != 0) angle *=   60;
-            final int n = fractionDigitsForDelta(Math.ulp(angle), false) - 1;
-            if (n < maximumFractionDigits) {
-                maximumFractionDigits = Math.max(minimumFractionDigits, n);
-            }
-        }
-        /*
+         * At this point the 'degrees', 'minutes' and 'seconds' variables contain the final values to format.
          * The following loop will format fields from DEGREES_FIELD to SECONDS_FIELD inclusive.
          * The NumberFormat will be reconfigured at each iteration.
          */
@@ -1039,12 +1033,16 @@ public class AngleFormat extends Format 
                 if (suffix != null) {
                     toAppendTo.append(suffix);
                 }
-                it.addFieldLimit(Field.forCode(field), hasMore
-                        ? (Number) Integer.valueOf((int) Math.round(value))
-                        : (Number) Float.valueOf((float) value), startPosition);
-                // The 'valueOf(…)' was for information purpose only. We use Float instead of Double
-                // because we don't want to give a false impression of accuracy (when formatting the
-                // seconds field, at least the 10 last bits of the double value are non-significant).
+                final Number userObject;
+                if (hasMore) {
+                    userObject = Integer.valueOf((int) Math.round(value));
+                } else {
+                    // Use Float instead of Double because we don't want to give a false impression of accuracy
+                    // (when formatting the seconds field, at least the 10 last bits of the 'double' value are
+                    // non-significant).
+                    userObject = Float.valueOf((float) value);
+                }
+                it.addFieldLimit(Field.forCode(field), userObject, startPosition);
             } else {
                 toAppendTo = numberFormat.format(value, toAppendTo, dummyFieldPosition());
                 if (suffix != null) {
@@ -1746,6 +1744,8 @@ BigBoss:    switch (skipSuffix(source, p
 
     /**
      * Returns a clone of this {@code AngleFormat}.
+     *
+     * @return A clone of this format.
      */
     @Override
     public AngleFormat clone() {

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -25,7 +25,7 @@ import org.apache.sis.util.Immutable;
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3 (derived from geotk-1.0)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see Longitude
@@ -98,4 +98,29 @@ public final class Latitude extends Angl
     final double maximum() {
         return 90;
     }
+
+    /**
+     * Returns the given latitude value clamped to the [{@linkplain #MIN_VALUE -90} … {@linkplain #MAX_VALUE 90}]° range.
+     * If the given value is outside the latitude range, then this method replaces it by ±90° with the same sign than the
+     * given φ value.
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>{@linkplain Double#NaN NaN} values are returned unchanged</li>
+     *   <li>±∞ are mapped to ±90° (with the same sign)</li>
+     *   <li>±0 are returned unchanged (i.e. the sign of negative and positive zero is preserved)</li>
+     * </ul>
+     *
+     * @param  φ The latitude value in decimal degrees.
+     * @return The given value clamped to the [-90 … 90]° range, or NaN if the given value was NaN.
+     *
+     * @see Longitude#normalize(double)
+     *
+     * @since 0.4
+     */
+    public static double clamp(final double φ) {
+        if (φ < MIN_VALUE) return MIN_VALUE;
+        if (φ > MAX_VALUE) return MAX_VALUE;
+        return φ;
+    }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -25,7 +25,7 @@ import org.apache.sis.util.Immutable;
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3 (derived from geotk-1.0)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see Latitude
@@ -54,6 +54,7 @@ public final class Longitude extends Ang
 
     /**
      * Construct a new longitude with the specified angular value.
+     * This constructor does <strong>not</strong> {@linkplain #normalize(double) normalize} the given value.
      *
      * @param λ Longitude value in decimal degrees.
      */
@@ -89,4 +90,28 @@ public final class Longitude extends Ang
     final char hemisphere(final boolean negative) {
         return negative ? 'W' : 'E';
     }
+
+    /**
+     * Returns the given longitude value normalized to the [{@linkplain #MIN_VALUE -180} … {@linkplain #MAX_VALUE 180})°
+     * range (upper value is exclusive). If the given value is outside the longitude range, then this method adds or
+     * subtracts a multiple of 360° in order to bring back the value to that range.
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>{@linkplain Double#NaN NaN} values are returned unchanged</li>
+     *   <li>±∞ are mapped to NaN</li>
+     *   <li>±180° are both mapped to -180° (i.e. the range is from -180° inclusive to +180° <em>exclusive</em>)</li>
+     *   <li>±0 are returned unchanged (i.e. the sign of negative and positive zero is preserved)</li>
+     * </ul>
+     *
+     * @param  λ The longitude value in decimal degrees.
+     * @return The given value normalized to the [-180 … 180)° range, or NaN if the given value was NaN of infinite.
+     *
+     * @see Latitude#clamp(double)
+     *
+     * @since 0.4
+     */
+    public static double normalize(final double λ) {
+        return λ - Math.floor((λ - MIN_VALUE) / (MAX_VALUE - MIN_VALUE)) * (MAX_VALUE - MIN_VALUE);
+    }
 }

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -39,11 +39,42 @@ import org.apache.sis.internal.jdk8.Func
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final strictfp class CollectionsExtTest extends TestCase {
     /**
+     * Tests {@link CollectionsExt#nonNullArraySet(String, Object, Object[])}.
+     */
+    @Test
+    public void testNonNullArraySet() {
+        final String name = "test";
+        final String[] emptyArray = new String[0];
+        assertSame(emptyArray,
+                CollectionsExt.nonNullArraySet(name, null, emptyArray));
+        assertSame(emptyArray,
+                CollectionsExt.nonNullArraySet(name, emptyArray, emptyArray));
+        assertArrayEquals(new String[] {"A"},
+                CollectionsExt.nonNullArraySet(name, "A", emptyArray));
+        assertArrayEquals(new String[] {"A"},
+                CollectionsExt.nonNullArraySet(name, new String[] {"A", null, "A"}, emptyArray));
+        assertArrayEquals(new String[] {"B", "A", "C"},
+                CollectionsExt.nonNullArraySet(name, new String[] {"B", "A", "B", "C", null, "A"}, emptyArray));
+        /*
+         * Verify that an exception is thrown in case of illegal value type.  Note that the Object[] type
+         * could be accepted if all elements are String instances, however the current method contract is
+         * to not accept them, so we will ensure that.
+         */
+        try {
+            CollectionsExt.nonNullArraySet(name, new Object[] {"A"}, emptyArray);
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains(name));
+            assertTrue(message, message.contains("Object[]"));
+        }
+    }
+
+    /**
      * Tests {@link CollectionsExt#addToMultiValuesMap(Map, Object, Object)}.
      */
     @Test

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -55,9 +55,10 @@ public final strictfp class DoubleDouble
     /**
      * The tolerance factor (as a multiplicand) for the multiplication and division operations.
      * This is a tolerance factor in units of {@link DoubleDouble#error} ULP, so even a "scary"
-     * factor like 1E+4 should be very small compared to the {@link DoubleDouble#value}.
+     * factor like 1E+5 should be very small compared to the {@link DoubleDouble#value}.
+     * This is the worst case error found empirically - most errors are smaller than that.
      */
-    private static final double PRODUCT_TOLERANCE_FACTOR = 10000;
+    private static final double PRODUCT_TOLERANCE_FACTOR = 100000;
 
     /**
      * Tolerance threshold for strict comparisons of floating point values.
@@ -81,7 +82,7 @@ public final strictfp class DoubleDouble
      * Fetches the next {@code DoubleDouble} random values and store them in the given object.
      */
     private void nextRandom(final DoubleDouble dd) {
-        dd.setToSum(nextRandom(), nextRandom());
+        dd.setToSum(nextRandom(), random.nextDouble());
     }
 
     /**
@@ -252,6 +253,42 @@ public final strictfp class DoubleDouble
     }
 
     /**
+     * Tests {@link DoubleDouble#sqrt()} first with the square root of 2, then with random values.
+     * In the {@code sqrt(2)} case:
+     *
+     * <ul>
+     *   <li>The error using {@code double} arithmetic is approximatively 1E-16.</li>
+     *   <li>The error using double-double arithmetic is expected to be slightly less that 1E-32.</li>
+     * </ul>
+     */
+    @Test
+    @DependsOnMethod({"testMultiply", "testDivide"})
+    public void testSqrt() {
+        final BigDecimal SQRT2 = new BigDecimal("1.414213562373095048801688724209698");
+        final DoubleDouble dd = new DoubleDouble(2, 0);
+        dd.sqrt();
+        assertNormalizedAndEquals(sqrt(2), dd);
+        assertEquals(0, SQRT2.subtract(toBigDecimal(dd)).doubleValue(), 1E-32);
+        /*
+         * If we have been able to compute √2, now test with random values.
+         * Since the range of values is approximatively [-1000 … 1000], use
+         * a tolerance value 1000 time the one that we used for √2.
+         */
+        for (int i=0; i<NUMBER_OF_REPETITIONS; i++) {
+            nextRandom(dd);
+            if (dd.value < 0) {
+                dd.negate();
+            }
+            final double value = dd.value;
+            final double error = dd.error;
+            dd.multiply(dd);
+            dd.sqrt();
+            dd.subtract(value, error);
+            assertEquals(0, dd.doubleValue(), 1E-29);
+        }
+    }
+
+    /**
      * List of all {@link DoubleDouble#VALUES} as string decimal representation.
      */
     private static final String[] PREDEFINED_VALUES = {
@@ -271,6 +308,7 @@ public final strictfp class DoubleDouble
          "0.9",
          "0.9144",
          "1.111111111111111111111111111111111",
+         "1.414213562373095048801688724209698",
          "1.570796326794896619231321691639751",
          "1.8288",
          "2.356194490192344928846982537459627",
@@ -343,4 +381,20 @@ public final strictfp class DoubleDouble
         assertTrue("toRadians", DoubleDouble.errorForWellKnownValue(PI / 180)     != 0);
         assertTrue("toRadians", DoubleDouble.errorForWellKnownValue(toRadians(1)) != 0);
     }
+
+    /**
+     * Tests the {@code DoubleDouble.createFoo()} methods.
+     */
+    @Test
+    @DependsOnMethod("testErrorForWellKnownValue")
+    public void testCreate() {
+        for (int i=0; ; i++) {
+            final DoubleDouble dd;
+            switch (i) {
+                case 0:  dd = DoubleDouble.createDegreesToRadians(); break;
+                default: return; // Test done.
+            }
+            assertEquals(DoubleDouble.errorForWellKnownValue(dd.value), dd.error, STRICT);
+        }
+    }
 }

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -22,8 +22,13 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.DependsOnMethod;
 
 import static org.junit.Assert.*;
+import static java.lang.Double.*;
 import static org.apache.sis.math.MathFunctions.*;
 import static org.apache.sis.util.ArraysExt.isSorted;
+import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
+
+// Related to JDK8
+import org.apache.sis.internal.jdk8.JDK8;
 
 
 /**
@@ -31,7 +36,7 @@ import static org.apache.sis.util.Arrays
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 @DependsOn(org.apache.sis.util.ArraysExtTest.class)
@@ -42,6 +47,13 @@ public final strictfp class MathFunction
     private static final double EPS = 1E-12;
 
     /**
+     * The maximal exponent value such as {@code parseDouble("1E+304")} still a finite number.
+     *
+     * @see Double#MAX_VALUE
+     */
+    private static final int MAX_E10 = 308;
+
+    /**
      * Highest prime number representable as a signed {@code short}.
      */
     private static final int HIGHEST_SHORT_PRIME = 32749;
@@ -52,26 +64,32 @@ public final strictfp class MathFunction
     private static final int LOWEST_USHORT_PRIME = 32771;
 
     /**
+     * Verifies the values of {@link MathFunctions#SQRT_2} and {@link MathFunctions#LOG10_2}.
+     */
+    @Test
+    public void testConstants() {
+        assertEquals(StrictMath.sqrt (2), MathFunctions.SQRT_2,  0);
+        assertEquals(StrictMath.log10(2), MathFunctions.LOG10_2, 0);
+        assertEquals(0,  parseDouble("1E" +  E10_FOR_ZERO), 0);
+        assertTrue  (0 < parseDouble("1E" + (E10_FOR_ZERO + 1)));
+        assertTrue  (POSITIVE_INFINITY > parseDouble("1E" +  MAX_E10));
+        assertEquals(POSITIVE_INFINITY,  parseDouble("1E" + (MAX_E10 + 1)), 0);
+    }
+
+    /**
      * Tests {@link MathFunctions#truncate(double)}.
      */
     @Test
+    @DependsOnMethod({"testIsPositiveZero", "testIsNegativeZero"})
     public void testTruncate() {
         assertEquals(+4.0, truncate(+4.9), 0);
         assertEquals(-4.0, truncate(-4.9), 0);
         assertEquals(+0.0, truncate(+0.1), 0);
         assertEquals(-0.0, truncate(-0.1), 0);
-        assertEquals("Positive zero",
-                Double.doubleToLongBits(+0.0),
-                Double.doubleToLongBits(truncate(+0.5)));
-        assertEquals("Negative zero",
-                Double.doubleToLongBits(-0.0),
-                Double.doubleToLongBits(truncate(-0.5)));
-        assertEquals("Positive zero",
-                Double.doubleToLongBits(+0.0),
-                Double.doubleToLongBits(truncate(+0.0)));
-        assertEquals("Negative zero",
-                Double.doubleToLongBits(-0.0),
-                Double.doubleToLongBits(truncate(-0.0)));
+        assertTrue("Positive zero", isPositiveZero(truncate(+0.5)));
+        assertTrue("Negative zero", isNegativeZero(truncate(-0.5)));
+        assertTrue("Positive zero", isPositiveZero(truncate(+0.0)));
+        assertTrue("Negative zero", isNegativeZero(truncate(-0.0)));
     }
 
     /**
@@ -126,11 +144,91 @@ public final strictfp class MathFunction
         assertEquals(-22,  fractionDigitsForDelta(9.6E+23,  true)); // Special case
         assertEquals(-300, fractionDigitsForDelta(1.1E+300, true));
 
-        // Special cases.
-        assertEquals(0,  fractionDigitsForDelta(0, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.NaN, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.POSITIVE_INFINITY, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.NEGATIVE_INFINITY, true));
+        // Other cases.
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForDelta(0,                 false));
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForDelta(MIN_VALUE,         false));
+        assertEquals(           16, fractionDigitsForDelta(Math.ulp(1.0),     false));
+        assertEquals(           15, fractionDigitsForDelta(Math.ulp(45.0),    false));
+        assertEquals(            0, fractionDigitsForDelta(NaN,               false));
+        assertEquals(            0, fractionDigitsForDelta(POSITIVE_INFINITY, false));
+        assertEquals(            0, fractionDigitsForDelta(NEGATIVE_INFINITY, false));
+    }
+
+    /**
+     * Tests {@link MathFunctions#fractionDigitsForValue(double)}.
+     */
+    @Test
+    public void testFractionDigitsForValue() {
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForValue(0));
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForValue(MIN_VALUE));
+        assertEquals(           16, fractionDigitsForValue(1));
+        assertEquals(           15, fractionDigitsForValue(45));
+        assertEquals(            0, fractionDigitsForValue(NaN));
+        assertEquals(            0, fractionDigitsForValue(POSITIVE_INFINITY));
+        assertEquals(            0, fractionDigitsForValue(NEGATIVE_INFINITY));
+        for (int i=E10_FOR_ZERO; i<=MAX_E10; i++) {
+            final double value = pow10(i);
+            final double accuracy = pow10(-fractionDigitsForValue(value));
+            assertEquals("Shall not be greater than ULP", 0, accuracy, StrictMath.ulp(value));
+        }
+        for (int i=MIN_EXPONENT; i<=MAX_EXPONENT; i++) {
+            final double value = StrictMath.scalb(1, i);
+            final double accuracy = pow10(-fractionDigitsForValue(value));
+            assertEquals("Shall not be greater than ULP", 0, accuracy, StrictMath.ulp(value));
+        }
+    }
+
+    /**
+     * Tests the {@link MathFunctions#getExponent(double)} method.
+     * This method performs two tests:
+     *
+     * <ul>
+     *   <li>First, tests with a few normal (non-subnormal) numbers.
+     *       The result shall be identical to {@link StrictMath#getExponent(double)}.</li>
+     *   <li>Then, test with a few sub-normal numbers.</li>
+     * </ul>
+     */
+    @Test
+    public void testGetExponent() {
+        final double[] normalValues = {
+            1E+300, 1E+200, 1E+100, 1E+10, 50, 20, 1, 1E-10, 1E-100, 1E-200, 1E-300,
+            POSITIVE_INFINITY,
+            NEGATIVE_INFINITY,
+            NaN,
+            MAX_VALUE,
+            MIN_NORMAL
+        };
+        for (final double value : normalValues) {
+            assertEquals(StrictMath.getExponent(value), getExponent(value));
+        }
+        /*
+         * Tests sub-normal values. We expect:
+         *
+         *   getExponent(MIN_NORMAL    )  ==  MIN_EXPONENT
+         *   getExponent(MIN_NORMAL / 2)  ==  MIN_EXPONENT - 1
+         *   getExponent(MIN_NORMAL / 4)  ==  MIN_EXPONENT - 2
+         *   getExponent(MIN_NORMAL / 8)  ==  MIN_EXPONENT - 3
+         *   etc.
+         */
+        for (int i=0; i<=SIGNIFICAND_SIZE; i++) {
+            assertEquals(MIN_EXPONENT - i, getExponent(MIN_NORMAL / (1L << i)));
+        }
+        assertEquals(MIN_EXPONENT - 1,                    getExponent(JDK8.nextDown(MIN_NORMAL)));
+        assertEquals(MIN_EXPONENT - SIGNIFICAND_SIZE,     getExponent(MIN_VALUE));
+        assertEquals(MIN_EXPONENT - SIGNIFICAND_SIZE - 1, getExponent(0));
+        /*
+         * Tests consistency with scalb, as documented in MathFunctions.getExponent(double) javadoc.
+         */
+        for (int i = MIN_EXPONENT - SIGNIFICAND_SIZE - 1; i <= MAX_EXPONENT + 1; i++) {
+            assertEquals(i, getExponent(StrictMath.scalb(1.0, i)));
+        }
+        /*
+         * Tests consistency with log10, as documented in MathFunctions.getExponent(double) javadoc.
+         */
+        for (int i = MIN_EXPONENT - SIGNIFICAND_SIZE; i <= MAX_EXPONENT; i++) {
+            assertEquals(StrictMath.floor(StrictMath.log10(StrictMath.scalb(1.0, i))),
+                         StrictMath.floor(LOG10_2 * i /* i = getExponent(value) */), 0);
+        }
     }
 
     /**
@@ -140,8 +238,8 @@ public final strictfp class MathFunction
      */
     @Test
     public void testPow10() {
-        for (int i=-304; i<=304; i++) { // Range of allowed exponents in base 10.
-            assertEquals(Double.parseDouble("1E"+i), pow10((double) i), 0);
+        for (int i=E10_FOR_ZERO; i<=MAX_E10; i++) { // Range of allowed exponents in base 10.
+            assertEquals(parseDouble("1E"+i), pow10((double) i), 0);
         }
     }
 
@@ -154,14 +252,34 @@ public final strictfp class MathFunction
             final double x = 0.1 * i;
             final double y = atanh(x);
             switch (i) {
-                case -10: assertEquals(Double.NEGATIVE_INFINITY, y, EPS); break;
-                default:  assertEquals(x, StrictMath.tanh(y),       EPS); break;
-                case +10: assertEquals(Double.POSITIVE_INFINITY, y, EPS); break;
+                case -10: assertEquals(NEGATIVE_INFINITY, y,  EPS); break;
+                default:  assertEquals(x, StrictMath.tanh(y), EPS); break;
+                case +10: assertEquals(POSITIVE_INFINITY, y,  EPS); break;
             }
         }
     }
 
     /**
+     * Tests {@link MathFunctions#isPositiveZero(double)}.
+     */
+    @Test
+    public void testIsPositiveZero() {
+        assertTrue (isPositiveZero(+0.0));
+        assertFalse(isPositiveZero(-0.0));
+        assertFalse(isPositiveZero( NaN));
+    }
+
+    /**
+     * Tests {@link MathFunctions#isNegativeZero(double)}.
+     */
+    @Test
+    public void testIsNegativeZero() {
+        assertTrue (isNegativeZero(-0.0));
+        assertFalse(isNegativeZero(+0.0));
+        assertFalse(isNegativeZero( NaN));
+    }
+
+    /**
      * Tests the {@link MathFunctions#xorSign(double, double)} method.
      */
     @Test

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -136,6 +136,7 @@ public final strictfp class AngleFormatT
         assertEquals( "12°30.0", formatAndParse(f, new Angle( 12.50)));
         assertEquals("-10°15.0", formatAndParse(f, new Angle(-10.25)));
         assertEquals("-00°00.6", formatAndParse(f, new Angle( -0.01)));
+        assertEquals( "89°01.0", formatAndParse(f, new Angle( 89.01666666666667)));
     }
 
     /**
@@ -151,6 +152,7 @@ public final strictfp class AngleFormatT
         assertEquals( "12°30′56.250″", formatAndParse(f, new Angle( 12.515625)));
         assertEquals("-12°30′56.250″", formatAndParse(f, new Angle(-12.515625)));
         assertEquals("-00°00′36.000″", formatAndParse(f, new Angle( -0.01)));
+        assertEquals( "89°01′00.000″", formatAndParse(f, new Angle( 89.01666666666667)));
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -21,11 +21,13 @@ import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
+import static java.lang.Double.NaN;
+import static java.lang.Double.doubleToLongBits;
 import static org.junit.Assert.*;
 
 
 /**
- * Tests the {@link Angle} class.
+ * Tests the {@link Angle}, {@link Longitude} and {@link Latitude} classes.
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.0)
@@ -44,6 +46,7 @@ public final strictfp class AngleTest ex
         assertEquals("45°30′N",       new Latitude (45.5).toString());
         assertEquals("45°30′E",       new Longitude(45.5).toString());
         assertEquals("45°30′56.25″E", new Longitude(45.515625).toString());
+        assertEquals("89°01′N",       new Latitude (89.01666666666667).toString());
 
         // Angle out of expected range.
         assertEquals( "720.0°E", new Longitude( 720).toString());
@@ -87,4 +90,38 @@ public final strictfp class AngleTest ex
         assertEquals("N",         String.format(Locale.FRANCE,  "%1.1s", new Latitude(5.51)));
         assertEquals(" ",         String.format(Locale.FRANCE,  "%1.0s", new Latitude(5.51)));
     }
+
+    /**
+     * Tests {@link Latitude#clamp(double)}.
+     */
+    @Test
+    public void testClamp() {
+        assertEquals( 45, Latitude.clamp( 45), 0);
+        assertEquals(-45, Latitude.clamp(-45), 0);
+        assertEquals( 90, Latitude.clamp( 95), 0);
+        assertEquals(-90, Latitude.clamp(-95), 0);
+        assertEquals(NaN, Latitude.clamp(NaN), 0);
+        assertEquals( 90, Latitude.clamp(Double.POSITIVE_INFINITY), 0);
+        assertEquals(-90, Latitude.clamp(Double.NEGATIVE_INFINITY), 0);
+        assertEquals(doubleToLongBits(+0.0), doubleToLongBits(Latitude.clamp(+0.0)));
+        assertEquals(doubleToLongBits(-0.0), doubleToLongBits(Latitude.clamp(-0.0))); // Sign shall be preserved.
+    }
+
+    /**
+     * Tests {@link Longitude#normalize(double)}.
+     */
+    @Test
+    public void testNormalize() {
+        assertEquals( 120, Longitude.normalize( 120), 0);
+        assertEquals(-120, Longitude.normalize(-120), 0);
+        assertEquals(-160, Longitude.normalize( 200), 0);
+        assertEquals( 160, Longitude.normalize(-200), 0);
+        assertEquals(-180, Longitude.normalize(-180), 0);
+        assertEquals(-180, Longitude.normalize( 180), 0); // Upper value shall be exclusive.
+        assertEquals(NaN,  Longitude.normalize( NaN), 0);
+        assertEquals(NaN,  Longitude.normalize(Double.POSITIVE_INFINITY), 0);
+        assertEquals(NaN,  Longitude.normalize(Double.NEGATIVE_INFINITY), 0);
+        assertEquals(doubleToLongBits(+0.0), doubleToLongBits(Longitude.normalize(+0.0)));
+        assertEquals(doubleToLongBits(-0.0), doubleToLongBits(Longitude.normalize(-0.0))); // Sign shall be preserved.
+    }
 }

Modified: sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java?rev=1535873&r1=1535872&r2=1535873&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java [UTF-8] Fri Oct 25 21:12:13 2013
@@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream;
 import javax.swing.tree.TreeNode;
 import javax.xml.parsers.ParserConfigurationException;
 import org.xml.sax.SAXException;
+import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ComparisonMode;
@@ -53,6 +54,33 @@ public strictfp class Assert extends org
     }
 
     /**
+     * Asserts that the given matrix is equals to the expected one, up to the given tolerance value.
+     *
+     * @param message   Header of the exception message in case of failure, or {@code null} if none.
+     * @param expected  The expected matrix.
+     * @param actual    The matrix to compare.
+     * @param tolerance The tolerance threshold.
+     */
+    public static void assertMatrixEquals(final String message, final Matrix expected, final Matrix actual, final double tolerance) {
+        if (TestCase.PENDING_NEXT_GEOAPI_RELEASE) {
+            // TODO: Remove this method, so we inherit the GeoAPI one instead.
+        }
+        final int numRow = actual.getNumRow();
+        final int numCol = actual.getNumCol();
+        assertEquals("numRow", expected.getNumRow(), numRow);
+        assertEquals("numCol", expected.getNumCol(), numCol);
+        for (int j=0; j<numRow; j++) {
+            for (int i=0; i<numCol; i++) {
+                final double e = expected.getElement(j,i);
+                final double a = actual.getElement(j,i);
+                if (!(StrictMath.abs(e - a) <= tolerance) && Double.doubleToLongBits(a) != Double.doubleToLongBits(e)) {
+                    fail("Matrix.getElement(" + j + ", " + i + "): expected " + e + " but got " + a);
+                }
+            }
+        }
+    }
+
+    /**
      * Asserts that the two given objects are not equal.
      * This method tests all {@link ComparisonMode} except {@code DEBUG}.
      *



Mime
View raw message