commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From l..@apache.org
Subject [2/4] git commit: MATH-1138 #comment BicubicSplineInterpolator Maintenance
Date Tue, 21 Oct 2014 07:55:35 GMT
MATH-1138 #comment BicubicSplineInterpolator Maintenance

Moved the new bi-cubic interpolator functions to new Piecewise* version
of the same files.  Restored the original bi-cubic functions and
restored the tricubic function to use the original functions as well


Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/31fae643
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/31fae643
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/31fae643

Branch: refs/heads/master
Commit: 31fae6431438e26d6b47b988164847048ceab314
Parents: e89a80d
Author: Hank Grabowski <hank@applieddefense.com>
Authored: Mon Oct 20 21:31:39 2014 -0400
Committer: Luc Maisonobe <luc@apache.org>
Committed: Tue Oct 21 09:49:21 2014 +0200

----------------------------------------------------------------------
 .../BicubicSplineInterpolatingFunction.java     | 620 ++++++++++++---
 .../BicubicSplineInterpolator.java              | 141 +++-
 ...ewiseBicubicSplineInterpolatingFunction.java | 195 +++++
 .../PiecewiseBicubicSplineInterpolator.java     |  64 ++
 ...hingPolynomialBicubicSplineInterpolator.java |   4 +-
 .../TricubicSplineInterpolator.java             |  35 +-
 .../BicubicSplineInterpolatingFunctionTest.java | 746 ++++++++++++++-----
 .../BicubicSplineInterpolatorTest.java          | 217 ++----
 ...eBicubicSplineInterpolatingFunctionTest.java | 280 +++++++
 .../PiecewiseBicubicSplineInterpolatorTest.java | 277 +++++++
 .../TricubicSplineInterpolatorTest.java         |   2 +-
 11 files changed, 2136 insertions(+), 445 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
index c0ce3c5..079e9fc 100644
--- a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
@@ -18,76 +18,143 @@ package org.apache.commons.math3.analysis.interpolation;
 
 import java.util.Arrays;
 import org.apache.commons.math3.analysis.BivariateFunction;
-import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
 import org.apache.commons.math3.exception.DimensionMismatchException;
-import org.apache.commons.math3.exception.InsufficientDataException;
 import org.apache.commons.math3.exception.NoDataException;
-import org.apache.commons.math3.exception.NullArgumentException;
 import org.apache.commons.math3.exception.OutOfRangeException;
 import org.apache.commons.math3.exception.NonMonotonicSequenceException;
 import org.apache.commons.math3.util.MathArrays;
 
 /**
- * Function that implements the <a
- * href="http://www.paulinternet.nl/?page=bicubic"> bicubic spline
- * interpolation</a>.
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
+ * bicubic spline interpolation</a>.
  *
  * @since 2.1
  */
 public class BicubicSplineInterpolatingFunction
     implements BivariateFunction {
+    /** Number of coefficients. */
+    private static final int NUM_COEFF = 16;
+    /**
+     * Matrix to compute the spline coefficients from the function values
+     * and function derivatives values
+     */
+    private static final double[][] AINV = {
+        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+        { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
+        { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
+        { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
+        { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+        { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
+        { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
+        { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
+        { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+        { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
+        { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
+        { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
+    };
 
     /** Samples x-coordinates */
     private final double[] xval;
-
     /** Samples y-coordinates */
     private final double[] yval;
-
     /** Set of cubic splines patching the whole data grid */
-    private final double[][] fval;
+    private final BicubicSplineFunction[][] splines;
+    /**
+     * Partial derivatives.
+     * The value of the first index determines the kind of derivatives:
+     * 0 = first partial derivatives wrt x
+     * 1 = first partial derivatives wrt y
+     * 2 = second partial derivatives wrt x
+     * 3 = second partial derivatives wrt y
+     * 4 = cross partial derivatives
+     */
+    private final BivariateFunction[][][] partialDerivatives;
 
     /**
      * @param x Sample values of the x-coordinate, in increasing order.
      * @param y Sample values of the y-coordinate, in increasing order.
-     * @param f Values of the function on every grid point. the expected number
-     *        of elements.
-     * @throws NonMonotonicSequenceException if {@code x} or {@code y} are not
-     *         strictly increasing.
-     * @throws NullArgumentException if any of the arguments are null
+     * @param f Values of the function on every grid point.
+     * @param dFdX Values of the partial derivative of function with respect
+     * to x on every grid point.
+     * @param dFdY Values of the partial derivative of function with respect
+     * to y on every grid point.
+     * @param d2FdXdY Values of the cross partial derivative of function on
+     * every grid point.
+     * @throws DimensionMismatchException if the various arrays do not contain
+     * the expected number of elements.
+     * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+     * not strictly increasing.
      * @throws NoDataException if any of the arrays has zero length.
-     * @throws DimensionMismatchException if the length of x and y don't match the row, column
-     *         height of f
      */
+    public BicubicSplineInterpolatingFunction(double[] x,
+                                              double[] y,
+                                              double[][] f,
+                                              double[][] dFdX,
+                                              double[][] dFdY,
+                                              double[][] d2FdXdY)
+        throws DimensionMismatchException,
+               NoDataException,
+               NonMonotonicSequenceException {
+        this(x, y, f, dFdX, dFdY, d2FdXdY, false);
+    }
 
-    public BicubicSplineInterpolatingFunction(double[] x, double[] y,
-                                              double[][] f)
-        throws DimensionMismatchException, NullArgumentException,
-        NoDataException, NonMonotonicSequenceException {
-
-        final int minimumLength = AkimaSplineInterpolator.MINIMUM_NUMBER_POINTS;
-
-        if (x == null || y == null || f == null || f[0] == null) {
-            throw new NullArgumentException();
-        }
-
+    /**
+     * @param x Sample values of the x-coordinate, in increasing order.
+     * @param y Sample values of the y-coordinate, in increasing order.
+     * @param f Values of the function on every grid point.
+     * @param dFdX Values of the partial derivative of function with respect
+     * to x on every grid point.
+     * @param dFdY Values of the partial derivative of function with respect
+     * to y on every grid point.
+     * @param d2FdXdY Values of the cross partial derivative of function on
+     * every grid point.
+     * @param initializeDerivatives Whether to initialize the internal data
+     * needed for calling any of the methods that compute the partial derivatives
+     * this function.
+     * @throws DimensionMismatchException if the various arrays do not contain
+     * the expected number of elements.
+     * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+     * not strictly increasing.
+     * @throws NoDataException if any of the arrays has zero length.
+     *
+     * @see #partialDerivativeX(double,double)
+     * @see #partialDerivativeY(double,double)
+     * @see #partialDerivativeXX(double,double)
+     * @see #partialDerivativeYY(double,double)
+     * @see #partialDerivativeXY(double,double)
+     */
+    public BicubicSplineInterpolatingFunction(double[] x,
+                                              double[] y,
+                                              double[][] f,
+                                              double[][] dFdX,
+                                              double[][] dFdY,
+                                              double[][] d2FdXdY,
+                                              boolean initializeDerivatives)
+        throws DimensionMismatchException,
+               NoDataException,
+               NonMonotonicSequenceException {
         final int xLen = x.length;
         final int yLen = y.length;
 
         if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
             throw new NoDataException();
         }
-
-        if (xLen < minimumLength || yLen < minimumLength ||
-            f.length < minimumLength || f[0].length < minimumLength) {
-            throw new InsufficientDataException();
-        }
-
         if (xLen != f.length) {
             throw new DimensionMismatchException(xLen, f.length);
         }
-
-        if (yLen != f[0].length) {
-            throw new DimensionMismatchException(yLen, f[0].length);
+        if (xLen != dFdX.length) {
+            throw new DimensionMismatchException(xLen, dFdX.length);
+        }
+        if (xLen != dFdY.length) {
+            throw new DimensionMismatchException(xLen, dFdY.length);
+        }
+        if (xLen != d2FdXdY.length) {
+            throw new DimensionMismatchException(xLen, d2FdXdY.length);
         }
 
         MathArrays.checkOrder(x);
@@ -95,7 +162,57 @@ public class BicubicSplineInterpolatingFunction
 
         xval = x.clone();
         yval = y.clone();
-        fval = f.clone();
+
+        final int lastI = xLen - 1;
+        final int lastJ = yLen - 1;
+        splines = new BicubicSplineFunction[lastI][lastJ];
+
+        for (int i = 0; i < lastI; i++) {
+            if (f[i].length != yLen) {
+                throw new DimensionMismatchException(f[i].length, yLen);
+            }
+            if (dFdX[i].length != yLen) {
+                throw new DimensionMismatchException(dFdX[i].length, yLen);
+            }
+            if (dFdY[i].length != yLen) {
+                throw new DimensionMismatchException(dFdY[i].length, yLen);
+            }
+            if (d2FdXdY[i].length != yLen) {
+                throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+            }
+            final int ip1 = i + 1;
+            for (int j = 0; j < lastJ; j++) {
+                final int jp1 = j + 1;
+                final double[] beta = new double[] {
+                    f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
+                    dFdX[i][j], dFdX[ip1][j], dFdX[i][jp1], dFdX[ip1][jp1],
+                    dFdY[i][j], dFdY[ip1][j], dFdY[i][jp1], dFdY[ip1][jp1],
+                    d2FdXdY[i][j], d2FdXdY[ip1][j], d2FdXdY[i][jp1], d2FdXdY[ip1][jp1]
+                };
+
+                splines[i][j] = new BicubicSplineFunction(computeSplineCoefficients(beta),
+                                                          initializeDerivatives);
+            }
+        }
+
+        if (initializeDerivatives) {
+            // Compute all partial derivatives.
+            partialDerivatives = new BivariateFunction[5][lastI][lastJ];
+
+            for (int i = 0; i < lastI; i++) {
+                for (int j = 0; j < lastJ; j++) {
+                    final BicubicSplineFunction bcs = splines[i][j];
+                    partialDerivatives[0][i][j] = bcs.partialDerivativeX();
+                    partialDerivatives[1][i][j] = bcs.partialDerivativeY();
+                    partialDerivatives[2][i][j] = bcs.partialDerivativeXX();
+                    partialDerivatives[3][i][j] = bcs.partialDerivativeYY();
+                    partialDerivatives[4][i][j] = bcs.partialDerivativeXY();
+                }
+            }
+        } else {
+            // Partial derivative methods cannot be used.
+            partialDerivatives = null;
+        }
     }
 
     /**
@@ -103,37 +220,13 @@ public class BicubicSplineInterpolatingFunction
      */
     public double value(double x, double y)
         throws OutOfRangeException {
-        int index;
-        PolynomialSplineFunction spline;
-        AkimaSplineInterpolator interpolator = new AkimaSplineInterpolator();
-        final int offset = 2;
-        final int count = offset + 3;
-        final int i = searchIndex(x, xval, offset, count);
-        final int j = searchIndex(y, yval, offset, count);
-
-        double xArray[] = new double[count];
-        double yArray[] = new double[count];
-        double zArray[] = new double[count];
-        double interpArray[] = new double[count];
-
-        for (index = 0; index < count; index++) {
-            xArray[index] = xval[i + index];
-            yArray[index] = yval[j + index];
-        }
-
-        for (int zIndex = 0; zIndex < count; zIndex++) {
-            for (index = 0; index < count; index++) {
-                zArray[index] = fval[i + index][j + zIndex];
-            }
-            spline = interpolator.interpolate(xArray, zArray);
-            interpArray[zIndex] = spline.value(x);
-        }
-
-        spline = interpolator.interpolate(yArray, interpArray);
+        final int i = searchIndex(x, xval);
+        final int j = searchIndex(y, yval);
 
-        double returnValue = spline.value(y);
+        final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+        final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
 
-        return returnValue;
+        return splines[i][j].value(xN, yN);
     }
 
     /**
@@ -145,7 +238,9 @@ public class BicubicSplineInterpolatingFunction
      * @since 3.3
      */
     public boolean isValidPoint(double x, double y) {
-        if (x < xval[0] || x > xval[xval.length - 1] || y < yval[0] ||
+        if (x < xval[0] ||
+            x > xval[xval.length - 1] ||
+            y < yval[0] ||
             y > yval[yval.length - 1]) {
             return false;
         } else {
@@ -154,42 +249,385 @@ public class BicubicSplineInterpolatingFunction
     }
 
     /**
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the first partial derivative with
+     * respect to x.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    public double partialDerivativeX(double x, double y)
+        throws OutOfRangeException {
+        return partialDerivative(0, x, y);
+    }
+    /**
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the first partial derivative with
+     * respect to y.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    public double partialDerivativeY(double x, double y)
+        throws OutOfRangeException {
+        return partialDerivative(1, x, y);
+    }
+    /**
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the second partial derivative with
+     * respect to x.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    public double partialDerivativeXX(double x, double y)
+        throws OutOfRangeException {
+        return partialDerivative(2, x, y);
+    }
+    /**
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the second partial derivative with
+     * respect to y.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    public double partialDerivativeYY(double x, double y)
+        throws OutOfRangeException {
+        return partialDerivative(3, x, y);
+    }
+    /**
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the second partial cross-derivative.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    public double partialDerivativeXY(double x, double y)
+        throws OutOfRangeException {
+        return partialDerivative(4, x, y);
+    }
+
+    /**
+     * @param which First index in {@link #partialDerivatives}.
+     * @param x x-coordinate.
+     * @param y y-coordinate.
+     * @return the value at point (x, y) of the selected partial derivative.
+     * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+     * the range defined by the boundary values of {@code xval} (resp.
+     * {@code yval}).
+     * @throws NullPointerException if the internal data were not initialized
+     * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+     *             double[][],double[][],double[][],boolean) constructor}).
+     */
+    private double partialDerivative(int which, double x, double y)
+        throws OutOfRangeException {
+        final int i = searchIndex(x, xval);
+        final int j = searchIndex(y, yval);
+
+        final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+        final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+        return partialDerivatives[which][i][j].value(xN, yN);
+    }
+
+    /**
      * @param c Coordinate.
      * @param val Coordinate samples.
-     * @param offset how far back from found value to offset for querying
-     * @param count total number of elements forward from beginning that will be
-     *        queried
-     * @return the index in {@code val} corresponding to the interval containing
-     *         {@code c}.
-     * @throws OutOfRangeException if {@code c} is out of the range defined by
-     *         the boundary values of {@code val}.
-     */
-    private int searchIndex(double c, double[] val, int offset, int count) {
-        int r = Arrays.binarySearch(val, c);
-
-        if (r == -1 || r == -val.length - 1) {
+     * @return the index in {@code val} corresponding to the interval
+     * containing {@code c}.
+     * @throws OutOfRangeException if {@code c} is out of the
+     * range defined by the boundary values of {@code val}.
+     */
+    private int searchIndex(double c, double[] val) {
+        final int r = Arrays.binarySearch(val, c);
+
+        if (r == -1 ||
+            r == -val.length - 1) {
             throw new OutOfRangeException(c, val[0], val[val.length - 1]);
         }
 
         if (r < 0) {
-            // "c" in within an interpolation sub-interval, which returns
-            // negative
-            // need to remove the negative sign for consistency
-            r = -r - offset - 1;
+            // "c" in within an interpolation sub-interval: Return the
+            // index of the sample at the lower end of the sub-interval.
+            return -r - 2;
+        }
+        final int last = val.length - 1;
+        if (r == last) {
+            // "c" is the last sample of the range: Return the index
+            // of the sample at the lower end of the last sub-interval.
+            return last - 1;
+        }
+
+        // "c" is another sample point.
+        return r;
+    }
+
+    /**
+     * Compute the spline coefficients from the list of function values and
+     * function partial derivatives values at the four corners of a grid
+     * element. They must be specified in the following order:
+     * <ul>
+     *  <li>f(0,0)</li>
+     *  <li>f(1,0)</li>
+     *  <li>f(0,1)</li>
+     *  <li>f(1,1)</li>
+     *  <li>f<sub>x</sub>(0,0)</li>
+     *  <li>f<sub>x</sub>(1,0)</li>
+     *  <li>f<sub>x</sub>(0,1)</li>
+     *  <li>f<sub>x</sub>(1,1)</li>
+     *  <li>f<sub>y</sub>(0,0)</li>
+     *  <li>f<sub>y</sub>(1,0)</li>
+     *  <li>f<sub>y</sub>(0,1)</li>
+     *  <li>f<sub>y</sub>(1,1)</li>
+     *  <li>f<sub>xy</sub>(0,0)</li>
+     *  <li>f<sub>xy</sub>(1,0)</li>
+     *  <li>f<sub>xy</sub>(0,1)</li>
+     *  <li>f<sub>xy</sub>(1,1)</li>
+     * </ul>
+     * where the subscripts indicate the partial derivative with respect to
+     * the corresponding variable(s).
+     *
+     * @param beta List of function values and function partial derivatives
+     * values.
+     * @return the spline coefficients.
+     */
+    private double[] computeSplineCoefficients(double[] beta) {
+        final double[] a = new double[NUM_COEFF];
+
+        for (int i = 0; i < NUM_COEFF; i++) {
+            double result = 0;
+            final double[] row = AINV[i];
+            for (int j = 0; j < NUM_COEFF; j++) {
+                result += row[j] * beta[j];
+            }
+            a[i] = result;
+        }
+
+        return a;
+    }
+}
+
+/**
+ * 2D-spline function.
+ *
+ */
+class BicubicSplineFunction implements BivariateFunction {
+    /** Number of points. */
+    private static final short N = 4;
+    /** Coefficients */
+    private final double[][] a;
+    /** First partial derivative along x. */
+    private final BivariateFunction partialDerivativeX;
+    /** First partial derivative along y. */
+    private final BivariateFunction partialDerivativeY;
+    /** Second partial derivative along x. */
+    private final BivariateFunction partialDerivativeXX;
+    /** Second partial derivative along y. */
+    private final BivariateFunction partialDerivativeYY;
+    /** Second crossed partial derivative. */
+    private final BivariateFunction partialDerivativeXY;
+
+    /**
+     * Simple constructor.
+     *
+     * @param coeff Spline coefficients.
+     */
+    public BicubicSplineFunction(double[] coeff) {
+        this(coeff, false);
+    }
+
+    /**
+     * Simple constructor.
+     *
+     * @param coeff Spline coefficients.
+     * @param initializeDerivatives Whether to initialize the internal data
+     * needed for calling any of the methods that compute the partial derivatives
+     * this function.
+     */
+    public BicubicSplineFunction(double[] coeff,
+                                 boolean initializeDerivatives) {
+        a = new double[N][N];
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < N; j++) {
+                a[i][j] = coeff[i * N + j];
+            }
+        }
+
+        if (initializeDerivatives) {
+            // Compute all partial derivatives functions.
+            final double[][] aX = new double[N][N];
+            final double[][] aY = new double[N][N];
+            final double[][] aXX = new double[N][N];
+            final double[][] aYY = new double[N][N];
+            final double[][] aXY = new double[N][N];
+
+            for (int i = 0; i < N; i++) {
+                for (int j = 0; j < N; j++) {
+                    final double c = a[i][j];
+                    aX[i][j] = i * c;
+                    aY[i][j] = j * c;
+                    aXX[i][j] = (i - 1) * aX[i][j];
+                    aYY[i][j] = (j - 1) * aY[i][j];
+                    aXY[i][j] = j * aX[i][j];
+                }
+            }
+
+            partialDerivativeX = new BivariateFunction() {
+                    public double value(double x, double y)  {
+                        final double x2 = x * x;
+                        final double[] pX = {0, 1, x, x2};
+
+                        final double y2 = y * y;
+                        final double y3 = y2 * y;
+                        final double[] pY = {1, y, y2, y3};
+
+                        return apply(pX, pY, aX);
+                    }
+                };
+            partialDerivativeY = new BivariateFunction() {
+                    public double value(double x, double y)  {
+                        final double x2 = x * x;
+                        final double x3 = x2 * x;
+                        final double[] pX = {1, x, x2, x3};
+
+                        final double y2 = y * y;
+                        final double[] pY = {0, 1, y, y2};
+
+                        return apply(pX, pY, aY);
+                    }
+                };
+            partialDerivativeXX = new BivariateFunction() {
+                    public double value(double x, double y)  {
+                        final double[] pX = {0, 0, 1, x};
+
+                        final double y2 = y * y;
+                        final double y3 = y2 * y;
+                        final double[] pY = {1, y, y2, y3};
+
+                        return apply(pX, pY, aXX);
+                    }
+                };
+            partialDerivativeYY = new BivariateFunction() {
+                    public double value(double x, double y)  {
+                        final double x2 = x * x;
+                        final double x3 = x2 * x;
+                        final double[] pX = {1, x, x2, x3};
+
+                        final double[] pY = {0, 0, 1, y};
+
+                        return apply(pX, pY, aYY);
+                    }
+                };
+            partialDerivativeXY = new BivariateFunction() {
+                    public double value(double x, double y)  {
+                        final double x2 = x * x;
+                        final double[] pX = {0, 1, x, x2};
+
+                        final double y2 = y * y;
+                        final double[] pY = {0, 1, y, y2};
+
+                        return apply(pX, pY, aXY);
+                    }
+                };
         } else {
-            r -= offset;
+            partialDerivativeX = null;
+            partialDerivativeY = null;
+            partialDerivativeXX = null;
+            partialDerivativeYY = null;
+            partialDerivativeXY = null;
         }
+    }
 
-        if (r < 0) {
-            r = 0;
+    /**
+     * {@inheritDoc}
+     */
+    public double value(double x, double y) {
+        if (x < 0 || x > 1) {
+            throw new OutOfRangeException(x, 0, 1);
+        }
+        if (y < 0 || y > 1) {
+            throw new OutOfRangeException(y, 0, 1);
         }
 
-        if ((r + count) >= val.length) {
-            // "c" is the last sample of the range: Return the index
-            // of the sample at the lower end of the last sub-interval.
-            r = val.length - count;
+        final double x2 = x * x;
+        final double x3 = x2 * x;
+        final double[] pX = {1, x, x2, x3};
+
+        final double y2 = y * y;
+        final double y3 = y2 * y;
+        final double[] pY = {1, y, y2, y3};
+
+        return apply(pX, pY, a);
+    }
+
+    /**
+     * Compute the value of the bicubic polynomial.
+     *
+     * @param pX Powers of the x-coordinate.
+     * @param pY Powers of the y-coordinate.
+     * @param coeff Spline coefficients.
+     * @return the interpolated value.
+     */
+    private double apply(double[] pX, double[] pY, double[][] coeff) {
+        double result = 0;
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < N; j++) {
+                result += coeff[i][j] * pX[i] * pY[j];
+            }
         }
 
-        return r;
+        return result;
+    }
+
+    /**
+     * @return the partial derivative wrt {@code x}.
+     */
+    public BivariateFunction partialDerivativeX() {
+        return partialDerivativeX;
+    }
+    /**
+     * @return the partial derivative wrt {@code y}.
+     */
+    public BivariateFunction partialDerivativeY() {
+        return partialDerivativeY;
+    }
+    /**
+     * @return the second partial derivative wrt {@code x}.
+     */
+    public BivariateFunction partialDerivativeXX() {
+        return partialDerivativeXX;
+    }
+    /**
+     * @return the second partial derivative wrt {@code y}.
+     */
+    public BivariateFunction partialDerivativeYY() {
+        return partialDerivativeYY;
+    }
+    /**
+     * @return the second partial cross-derivative.
+     */
+    public BivariateFunction partialDerivativeXY() {
+        return partialDerivativeXY;
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
index a973f81..5e2c92f 100644
--- a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
@@ -16,10 +16,12 @@
  */
 package org.apache.commons.math3.analysis.interpolation;
 
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
 import org.apache.commons.math3.exception.DimensionMismatchException;
 import org.apache.commons.math3.exception.NoDataException;
 import org.apache.commons.math3.exception.NonMonotonicSequenceException;
-import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
 import org.apache.commons.math3.util.MathArrays;
 
 /**
@@ -28,37 +30,144 @@ import org.apache.commons.math3.util.MathArrays;
  * @since 2.2
  */
 public class BicubicSplineInterpolator
-    implements BivariateGridInterpolator
-{
+    implements BivariateGridInterpolator {
+    /** Whether to initialize internal data used to compute the analytical
+        derivatives of the splines. */
+    private final boolean initializeDerivatives;
 
     /**
      * Default constructor.
+     * The argument {@link #BicubicSplineInterpolator(boolean) initializeDerivatives}
+     * is set to {@code false}.
      */
-    public BicubicSplineInterpolator()
-    {
+    public BicubicSplineInterpolator() {
+        this(false);
+    }
 
+    /**
+     * Creates an interpolator.
+     *
+     * @param initializeDerivatives Whether to initialize the internal data
+     * needed for calling any of the methods that compute the partial derivatives
+     * of the {@link BicubicSplineInterpolatingFunction function} returned from
+     * the call to {@link #interpolate(double[],double[],double[][]) interpolate}.
+     */
+    public BicubicSplineInterpolator(boolean initializeDerivatives) {
+        this.initializeDerivatives = initializeDerivatives;
     }
 
     /**
      * {@inheritDoc}
      */
-    public BicubicSplineInterpolatingFunction interpolate( final double[] xval, final double[] yval,
+    public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+                                                          final double[] yval,
                                                           final double[][] fval)
-        throws DimensionMismatchException, NullArgumentException, NoDataException, NonMonotonicSequenceException
-    {
-        if ( xval == null || yval == null || fval == null || fval[0] == null )
-        {
-            throw new NullArgumentException();
-        }
-
-        if ( xval.length == 0 || yval.length == 0 || fval.length == 0 )
-        {
+        throws NoDataException, DimensionMismatchException,
+               NonMonotonicSequenceException, NumberIsTooSmallException {
+        if (xval.length == 0 || yval.length == 0 || fval.length == 0) {
             throw new NoDataException();
         }
+        if (xval.length != fval.length) {
+            throw new DimensionMismatchException(xval.length, fval.length);
+        }
 
         MathArrays.checkOrder(xval);
         MathArrays.checkOrder(yval);
 
-        return new BicubicSplineInterpolatingFunction( xval, yval, fval );
+        final int xLen = xval.length;
+        final int yLen = yval.length;
+
+        // Samples (first index is y-coordinate, i.e. subarray variable is x)
+        // 0 <= i < xval.length
+        // 0 <= j < yval.length
+        // fX[j][i] = f(xval[i], yval[j])
+        final double[][] fX = new double[yLen][xLen];
+        for (int i = 0; i < xLen; i++) {
+            if (fval[i].length != yLen) {
+                throw new DimensionMismatchException(fval[i].length, yLen);
+            }
+
+            for (int j = 0; j < yLen; j++) {
+                fX[j][i] = fval[i][j];
+            }
+        }
+
+        final SplineInterpolator spInterpolator = new SplineInterpolator();
+
+        // For each line y[j] (0 <= j < yLen), construct a 1D spline with
+        // respect to variable x
+        final PolynomialSplineFunction[] ySplineX = new PolynomialSplineFunction[yLen];
+        for (int j = 0; j < yLen; j++) {
+            ySplineX[j] = spInterpolator.interpolate(xval, fX[j]);
+        }
+
+        // For each line x[i] (0 <= i < xLen), construct a 1D spline with
+        // respect to variable y generated by array fY_1[i]
+        final PolynomialSplineFunction[] xSplineY = new PolynomialSplineFunction[xLen];
+        for (int i = 0; i < xLen; i++) {
+            xSplineY[i] = spInterpolator.interpolate(yval, fval[i]);
+        }
+
+        // Partial derivatives with respect to x at the grid knots
+        final double[][] dFdX = new double[xLen][yLen];
+        for (int j = 0; j < yLen; j++) {
+            final UnivariateFunction f = ySplineX[j].derivative();
+            for (int i = 0; i < xLen; i++) {
+                dFdX[i][j] = f.value(xval[i]);
+            }
+        }
+
+        // Partial derivatives with respect to y at the grid knots
+        final double[][] dFdY = new double[xLen][yLen];
+        for (int i = 0; i < xLen; i++) {
+            final UnivariateFunction f = xSplineY[i].derivative();
+            for (int j = 0; j < yLen; j++) {
+                dFdY[i][j] = f.value(yval[j]);
+            }
+        }
+
+        // Cross partial derivatives
+        final double[][] d2FdXdY = new double[xLen][yLen];
+        for (int i = 0; i < xLen ; i++) {
+            final int nI = nextIndex(i, xLen);
+            final int pI = previousIndex(i);
+            for (int j = 0; j < yLen; j++) {
+                final int nJ = nextIndex(j, yLen);
+                final int pJ = previousIndex(j);
+                d2FdXdY[i][j] = (fval[nI][nJ] - fval[nI][pJ] -
+                                 fval[pI][nJ] + fval[pI][pJ]) /
+                    ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ]));
+            }
+        }
+
+        // Create the interpolating splines
+        return new BicubicSplineInterpolatingFunction(xval, yval, fval,
+                                                      dFdX, dFdY, d2FdXdY,
+                                                      initializeDerivatives);
+    }
+
+    /**
+     * Computes the next index of an array, clipping if necessary.
+     * It is assumed (but not checked) that {@code i >= 0}.
+     *
+     * @param i Index.
+     * @param max Upper limit of the array.
+     * @return the next index.
+     */
+    private int nextIndex(int i, int max) {
+        final int index = i + 1;
+        return index < max ? index : index - 1;
+    }
+    /**
+     * Computes the previous index of an array, clipping if necessary.
+     * It is assumed (but not checked) that {@code i} is smaller than the size
+     * of the array.
+     *
+     * @param i Index.
+     * @return the previous index.
+     */
+    private int previousIndex(int i) {
+        final int index = i - 1;
+        return index >= 0 ? index : 0;
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..7942b52
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java
@@ -0,0 +1,195 @@
+/*
+ * 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.commons.math3.analysis.interpolation;
+
+import java.util.Arrays;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the <a
+ * href="http://www.paulinternet.nl/?page=bicubic"> bicubic spline
+ * interpolation</a>.
+ *
+ * @since 2.1
+ */
+public class PiecewiseBicubicSplineInterpolatingFunction
+    implements BivariateFunction {
+
+    /** Samples x-coordinates */
+    private final double[] xval;
+
+    /** Samples y-coordinates */
+    private final double[] yval;
+
+    /** Set of cubic splines patching the whole data grid */
+    private final double[][] fval;
+
+    /**
+     * @param x Sample values of the x-coordinate, in increasing order.
+     * @param y Sample values of the y-coordinate, in increasing order.
+     * @param f Values of the function on every grid point. the expected number
+     *        of elements.
+     * @throws NonMonotonicSequenceException if {@code x} or {@code y} are not
+     *         strictly increasing.
+     * @throws NullArgumentException if any of the arguments are null
+     * @throws NoDataException if any of the arrays has zero length.
+     * @throws DimensionMismatchException if the length of x and y don't match the row, column
+     *         height of f
+     */
+
+    public PiecewiseBicubicSplineInterpolatingFunction(double[] x, double[] y,
+                                              double[][] f)
+        throws DimensionMismatchException, NullArgumentException,
+        NoDataException, NonMonotonicSequenceException {
+
+        final int minimumLength = AkimaSplineInterpolator.MINIMUM_NUMBER_POINTS;
+
+        if (x == null || y == null || f == null || f[0] == null) {
+            throw new NullArgumentException();
+        }
+
+        final int xLen = x.length;
+        final int yLen = y.length;
+
+        if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
+            throw new NoDataException();
+        }
+
+        if (xLen < minimumLength || yLen < minimumLength ||
+            f.length < minimumLength || f[0].length < minimumLength) {
+            throw new InsufficientDataException();
+        }
+
+        if (xLen != f.length) {
+            throw new DimensionMismatchException(xLen, f.length);
+        }
+
+        if (yLen != f[0].length) {
+            throw new DimensionMismatchException(yLen, f[0].length);
+        }
+
+        MathArrays.checkOrder(x);
+        MathArrays.checkOrder(y);
+
+        xval = x.clone();
+        yval = y.clone();
+        fval = f.clone();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public double value(double x, double y)
+        throws OutOfRangeException {
+        int index;
+        PolynomialSplineFunction spline;
+        AkimaSplineInterpolator interpolator = new AkimaSplineInterpolator();
+        final int offset = 2;
+        final int count = offset + 3;
+        final int i = searchIndex(x, xval, offset, count);
+        final int j = searchIndex(y, yval, offset, count);
+
+        double xArray[] = new double[count];
+        double yArray[] = new double[count];
+        double zArray[] = new double[count];
+        double interpArray[] = new double[count];
+
+        for (index = 0; index < count; index++) {
+            xArray[index] = xval[i + index];
+            yArray[index] = yval[j + index];
+        }
+
+        for (int zIndex = 0; zIndex < count; zIndex++) {
+            for (index = 0; index < count; index++) {
+                zArray[index] = fval[i + index][j + zIndex];
+            }
+            spline = interpolator.interpolate(xArray, zArray);
+            interpArray[zIndex] = spline.value(x);
+        }
+
+        spline = interpolator.interpolate(yArray, interpArray);
+
+        double returnValue = spline.value(y);
+
+        return returnValue;
+    }
+
+    /**
+     * Indicates whether a point is within the interpolation range.
+     *
+     * @param x First coordinate.
+     * @param y Second coordinate.
+     * @return {@code true} if (x, y) is a valid point.
+     * @since 3.3
+     */
+    public boolean isValidPoint(double x, double y) {
+        if (x < xval[0] || x > xval[xval.length - 1] || y < yval[0] ||
+            y > yval[yval.length - 1]) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * @param c Coordinate.
+     * @param val Coordinate samples.
+     * @param offset how far back from found value to offset for querying
+     * @param count total number of elements forward from beginning that will be
+     *        queried
+     * @return the index in {@code val} corresponding to the interval containing
+     *         {@code c}.
+     * @throws OutOfRangeException if {@code c} is out of the range defined by
+     *         the boundary values of {@code val}.
+     */
+    private int searchIndex(double c, double[] val, int offset, int count) {
+        int r = Arrays.binarySearch(val, c);
+
+        if (r == -1 || r == -val.length - 1) {
+            throw new OutOfRangeException(c, val[0], val[val.length - 1]);
+        }
+
+        if (r < 0) {
+            // "c" in within an interpolation sub-interval, which returns
+            // negative
+            // need to remove the negative sign for consistency
+            r = -r - offset - 1;
+        } else {
+            r -= offset;
+        }
+
+        if (r < 0) {
+            r = 0;
+        }
+
+        if ((r + count) >= val.length) {
+            // "c" is the last sample of the range: Return the index
+            // of the sample at the lower end of the last sub-interval.
+            r = val.length - count;
+        }
+
+        return r;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java
new file mode 100644
index 0000000..2063f85
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a piecewise-bicubic interpolating function.
+ *
+ * @since 2.2
+ */
+public class PiecewiseBicubicSplineInterpolator
+    implements BivariateGridInterpolator
+{
+
+    /**
+     * Default constructor.
+     */
+    public PiecewiseBicubicSplineInterpolator()
+    {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PiecewiseBicubicSplineInterpolatingFunction interpolate( final double[] xval, final double[] yval,
+                                                          final double[][] fval)
+        throws DimensionMismatchException, NullArgumentException, NoDataException, NonMonotonicSequenceException
+    {
+        if ( xval == null || yval == null || fval == null || fval[0] == null )
+        {
+            throw new NullArgumentException();
+        }
+
+        if ( xval.length == 0 || yval.length == 0 || fval.length == 0 )
+        {
+            throw new NoDataException();
+        }
+
+        MathArrays.checkOrder(xval);
+        MathArrays.checkOrder(yval);
+
+        return new PiecewiseBicubicSplineInterpolatingFunction( xval, yval, fval );
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
index 1c76035..ad1e7b2 100644
--- a/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
@@ -36,7 +36,7 @@ import org.apache.commons.math3.optim.SimpleVectorValueChecker;
  * @since 2.2
  */
 public class SmoothingPolynomialBicubicSplineInterpolator
-    extends BicubicSplineInterpolator {
+    extends PiecewiseBicubicSplineInterpolator {
     /** Fitter for x. */
     private final PolynomialFitter xFitter;
     /** Degree of the fitting polynomial. */
@@ -92,7 +92,7 @@ public class SmoothingPolynomialBicubicSplineInterpolator
      * {@inheritDoc}
      */
     @Override
-    public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+    public PiecewiseBicubicSplineInterpolatingFunction interpolate(final double[] xval,
                                                           final double[] yval,
                                                           final double[][] fval)
         throws NoDataException, NullArgumentException,

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
index 0faa274..cda6a33 100644
--- a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
@@ -76,7 +76,7 @@ public class TricubicSplineInterpolator
             }
         }
 
-        final BicubicSplineInterpolator bsi = new BicubicSplineInterpolator();
+        final BicubicSplineInterpolator bsi = new BicubicSplineInterpolator(true);
 
         // For each line x[i] (0 <= i < xLen), construct a 2D spline in y and z
         final BicubicSplineInterpolatingFunction[] xSplineYZ
@@ -103,13 +103,46 @@ public class TricubicSplineInterpolator
         final double[][][] dFdX = new double[xLen][yLen][zLen];
         final double[][][] dFdY = new double[xLen][yLen][zLen];
         final double[][][] d2FdXdY = new double[xLen][yLen][zLen];
+        for (int k = 0; k < zLen; k++) {
+            final BicubicSplineInterpolatingFunction f = zSplineXY[k];
+            for (int i = 0; i < xLen; i++) {
+                final double x = xval[i];
+                for (int j = 0; j < yLen; j++) {
+                    final double y = yval[j];
+                    dFdX[i][j][k] = f.partialDerivativeX(x, y);
+                    dFdY[i][j][k] = f.partialDerivativeY(x, y);
+                    d2FdXdY[i][j][k] = f.partialDerivativeXY(x, y);
+                }
+            }
+        }
 
         // Partial derivatives wrt y and wrt z
         final double[][][] dFdZ = new double[xLen][yLen][zLen];
         final double[][][] d2FdYdZ = new double[xLen][yLen][zLen];
+        for (int i = 0; i < xLen; i++) {
+            final BicubicSplineInterpolatingFunction f = xSplineYZ[i];
+            for (int j = 0; j < yLen; j++) {
+                final double y = yval[j];
+                for (int k = 0; k < zLen; k++) {
+                    final double z = zval[k];
+                    dFdZ[i][j][k] = f.partialDerivativeY(y, z);
+                    d2FdYdZ[i][j][k] = f.partialDerivativeXY(y, z);
+                }
+            }
+        }
 
         // Partial derivatives wrt x and wrt z
         final double[][][] d2FdZdX = new double[xLen][yLen][zLen];
+        for (int j = 0; j < yLen; j++) {
+            final BicubicSplineInterpolatingFunction f = ySplineZX[j];
+            for (int k = 0; k < zLen; k++) {
+                final double z = zval[k];
+                for (int i = 0; i < xLen; i++) {
+                    final double x = xval[i];
+                    d2FdZdX[i][j][k] = f.partialDerivativeXY(z, x);
+                }
+            }
+        }
 
         // Third partial cross-derivatives
         final double[][][] d3FdXdYdZ = new double[xLen][yLen][zLen];

http://git-wip-us.apache.org/repos/asf/commons-math/blob/31fae643/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
index 773a948..8c78aed 100644
--- a/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
+++ b/src/test/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunctionTest.java
@@ -16,147 +16,435 @@
  */
 package org.apache.commons.math3.analysis.interpolation;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import org.apache.commons.math3.exception.DimensionMismatchException;
-import org.apache.commons.math3.exception.InsufficientDataException;
-import org.apache.commons.math3.exception.NonMonotonicSequenceException;
-import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
 import org.apache.commons.math3.analysis.BivariateFunction;
 import org.apache.commons.math3.distribution.UniformRealDistribution;
 import org.apache.commons.math3.random.RandomGenerator;
 import org.apache.commons.math3.random.Well19937c;
-import org.apache.commons.math3.util.FastMath;
-import org.apache.commons.math3.util.Precision;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.Ignore;
 
 /**
  * Test case for the bicubic function.
+ * 
  */
-public final class BicubicSplineInterpolatingFunctionTest
-{
+public final class BicubicSplineInterpolatingFunctionTest {
     /**
      * Test preconditions.
      */
     @Test
-    public void testPreconditions()
-    {
-        double[] xval = new double[] { 3, 4, 5, 6.5, 7.5 };
-        double[] yval = new double[] { -4, -3, -1, 2.5, 3.5 };
+    public void testPreconditions() {
+        double[] xval = new double[] {3, 4, 5, 6.5};
+        double[] yval = new double[] {-4, -3, -1, 2.5};
         double[][] zval = new double[xval.length][yval.length];
 
         @SuppressWarnings("unused")
-        BicubicSplineInterpolatingFunction bcf = new BicubicSplineInterpolatingFunction( xval, yval, zval );
+        BivariateFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+                                                                           zval, zval, zval);
         
-        try
-        {
-            bcf = new BicubicSplineInterpolatingFunction( null, yval, zval );
-            Assert.fail( "Failed to detect x null pointer" );
-        }
-        catch ( NullArgumentException iae )
-        {
-            // Expected.
+        double[] wxval = new double[] {3, 2, 5, 6.5};
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(wxval, yval, zval, zval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[] wyval = new double[] {-4, -1, -1, 2.5};
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, wyval, zval, zval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[][] wzval = new double[xval.length][yval.length - 1];
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+
+        wzval = new double[xval.length - 1][yval.length];
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, wzval, zval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, wzval, zval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, wzval, zval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval, zval, zval, wzval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
     }
 
-        try
-        {
-            bcf = new BicubicSplineInterpolatingFunction( xval, null, zval );
-            Assert.fail( "Failed to detect y null pointer" );
+    /**
+     * Test for a plane.
+     * <p>
+     * z = 2 x - 3 y + 5
+     */
+    @Ignore@Test
+    public void testPlane() {
+        double[] xval = new double[] {3, 4, 5, 6.5};
+        double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+        // Function values
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x - 3 * y + 5;
+                }
+            };
+        double[][] zval = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                zval[i][j] = f.value(xval[i], yval[j]);
+            }
+        }
+        // Partial derivatives with respect to x
+        double[][] dZdX = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdX[i][j] = 2;
+            }
         }
-        catch ( NullArgumentException iae )
-        {
-            // Expected.
+        // Partial derivatives with respect to y
+        double[][] dZdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdY[i][j] = -3;
+            }
+        }
+        // Partial cross-derivatives
+        double[][] dZdXdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdXdY[i][j] = 0;
+            }
+        }
+
+        BivariateFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+                                                                           dZdX, dZdY, dZdXdY);
+        double x, y;
+        double expected, result;
+
+        x = 4;
+        y = -3;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("On sample point",
+                            expected, result, 1e-15);
+
+        x = 4.5;
+        y = -1.5;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("Half-way between sample points (middle of the patch)",
+                            expected, result, 0.3);
+
+        x = 3.5;
+        y = -3.5;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("Half-way between sample points (border of the patch)",
+                            expected, result, 0.3);
     }
 
-        try
-        {
-            bcf = new BicubicSplineInterpolatingFunction( xval, yval, null );
-            Assert.fail( "Failed to detect z null pointer" );
+    /**
+     * Test for a paraboloid.
+     * <p>
+     * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
+     */
+    @Ignore@Test
+    public void testParaboloid() {
+        double[] xval = new double[] {3, 4, 5, 6.5};
+        double[] yval = new double[] {-4, -3, -1, 2, 2.5};
+        // Function values
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 2 * x * x - 3 * y * y + 4 * x * y - 5;
+                }
+            };
+        double[][] zval = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                zval[i][j] = f.value(xval[i], yval[j]);
+            }
+        }
+        // Partial derivatives with respect to x
+        double[][] dZdX = new double[xval.length][yval.length];
+        BivariateFunction dfdX = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * (x + y);
+                }
+            };
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdX[i][j] = dfdX.value(xval[i], yval[j]);
+            }
         }
-        catch ( NullArgumentException iae )
-        {
-            // Expected.
+        // Partial derivatives with respect to y
+        double[][] dZdY = new double[xval.length][yval.length];
+        BivariateFunction dfdY = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * x - 6 * y;
+                }
+            };
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdY[i][j] = dfdY.value(xval[i], yval[j]);
+            }
+        }
+        // Partial cross-derivatives
+        double[][] dZdXdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdXdY[i][j] = 4;
+            }
+        }
+
+        BivariateFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+                                                                           dZdX, dZdY, dZdXdY);
+        double x, y;
+        double expected, result;
+        
+        x = 4;
+        y = -3;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("On sample point",
+                            expected, result, 1e-15);
+
+        x = 4.5;
+        y = -1.5;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("Half-way between sample points (middle of the patch)",
+                            expected, result, 2);
+
+        x = 3.5;
+        y = -3.5;
+        expected = f.value(x, y);
+        result = bcf.value(x, y);
+        Assert.assertEquals("Half-way between sample points (border of the patch)",
+                            expected, result, 2);
     }
 
-        try
-        {
-            double xval1[] = { 0.0, 1.0, 2.0, 3.0 };
-            bcf = new BicubicSplineInterpolatingFunction( xval1, yval, zval );
-            Assert.fail( "Failed to detect insufficient x data" );
+    /**
+     * Test for partial derivatives of {@link BicubicSplineFunction}.
+     * <p>
+     * f(x, y) = &Sigma;<sub>i</sub>&Sigma;<sub>j</sub> (i+1) (j+2) x<sup>i</sup> y<sup>j</sup>
+     */
+    @Ignore@Test
+    public void testSplinePartialDerivatives() {
+        final int N = 4;
+        final double[] coeff = new double[16];
+
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < N; j++) {
+                coeff[i + N * j] = (i + 1) * (j + 2);
             }
-        catch ( InsufficientDataException iae )
-        {
-            // Expected.
         }
 
-        try
-        {
-            double yval1[] = { 0.0, 1.0, 2.0, 3.0 };
-            bcf = new BicubicSplineInterpolatingFunction( xval, yval1, zval );
-            Assert.fail( "Failed to detect insufficient y data" );
+        final BicubicSplineFunction f = new BicubicSplineFunction(coeff);
+        BivariateFunction derivative;
+        final double x = 0.435;
+        final double y = 0.776;
+        final double tol = 1e-13;
+
+        derivative = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double y2 = y * y;
+                    final double y3 = y2 * y;
+                    final double yFactor = 2 + 3 * y + 4 * y2 + 5 * y3;
+                    return yFactor * (2 + 6 * x + 12 * x2);
                 }
-        catch ( InsufficientDataException iae )
-        {
-            // Expected.
+            };
+        Assert.assertEquals("dFdX", derivative.value(x, y),
+                            f.partialDerivativeX().value(x, y), tol);
+        
+        derivative = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double x3 = x2 * x;
+                    final double y2 = y * y;
+                    final double xFactor = 1 + 2 * x + 3 * x2 + 4 * x3;
+                    return xFactor * (3 + 8 * y + 15 * y2);
                 }
+            };
+        Assert.assertEquals("dFdY", derivative.value(x, y),
+                            f.partialDerivativeY().value(x, y), tol);
 
-        try
-        {
-            double zval1[][] = new double[4][4];
-            bcf = new BicubicSplineInterpolatingFunction( xval, yval, zval1 );
-            Assert.fail( "Failed to detect insufficient z data" );
+        derivative = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double y2 = y * y;
+                    final double y3 = y2 * y;
+                    final double yFactor = 2 + 3 * y + 4 * y2 + 5 * y3;
+                    return yFactor * (6 + 24 * x);
                 }
-        catch ( InsufficientDataException iae )
-        {
-            // Expected.
+            };
+        Assert.assertEquals("d2FdX2", derivative.value(x, y),
+                            f.partialDerivativeXX().value(x, y), tol);
+
+        derivative = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double x3 = x2 * x;
+                    final double xFactor = 1 + 2 * x + 3 * x2 + 4 * x3;
+                    return xFactor * (8 + 30 * y);
                 }
+            };
+        Assert.assertEquals("d2FdY2", derivative.value(x, y),
+                            f.partialDerivativeYY().value(x, y), tol);
 
-        try
-        {
-            double xval1[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
-            bcf = new BicubicSplineInterpolatingFunction( xval1, yval, zval );
-            Assert.fail( "Failed to detect data set array with different sizes." );
+        derivative = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double y2 = y * y;
+                    final double yFactor = 3 + 8 * y + 15 * y2;
+                    return yFactor * (2 + 6 * x + 12 * x2);
                 }
-        catch ( DimensionMismatchException iae )
-        {
-            // Expected.
+            };
+        Assert.assertEquals("d2FdXdY", derivative.value(x, y),
+                            f.partialDerivativeXY().value(x, y), tol);
     }
 
-        try
-        {
-            double yval1[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 };
-            bcf = new BicubicSplineInterpolatingFunction( xval, yval1, zval );
-            Assert.fail( "Failed to detect data set array with different sizes." );
+    /**
+     * Test that the partial derivatives computed from a
+     * {@link BicubicSplineInterpolatingFunction} match the input data.
+     * <p>
+     * f(x, y) = 5
+     *           - 3 x + 2 y
+     *           - x y + 2 x<sup>2</sup> - 3 y<sup>2</sup>
+     *           + 4 x<sup>2</sup> y - x y<sup>2</sup> - 3 x<sup>3</sup> + y<sup>3</sup>
+     */
+    @Ignore@Test
+    public void testMatchingPartialDerivatives() {
+        final int sz = 21;
+        double[] val = new double[sz];
+        // Coordinate values
+        final double delta = 1d / (sz - 1);
+        for (int i = 0; i < sz; i++) {
+            val[i] = i * delta;
+        }
+        // Function values
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double x3 = x2 * x;
+                    final double y2 = y * y;
+                    final double y3 = y2 * y;
+
+                    return 5
+                        - 3 * x + 2 * y
+                        - x * y + 2 * x2 - 3 * y2
+                        + 4 * x2 * y - x * y2 - 3 * x3 + y3;
+                }
+            };
+        double[][] fval = new double[sz][sz];
+        for (int i = 0; i < sz; i++) {
+            for (int j = 0; j < sz; j++) {
+                fval[i][j] = f.value(val[i], val[j]);
+            }
         }
-        catch ( DimensionMismatchException iae )
-        {
-            // Expected.
+        // Partial derivatives with respect to x
+        double[][] dFdX = new double[sz][sz];
+        BivariateFunction dfdX = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double y2 = y * y;                    
+                    return - 3 - y + 4 * x + 8 * x * y - y2 - 9 * x2;
                 }
-
-        // X values not sorted.
-        try
-        {
-            double xval1[] = { 0.0, 1.0, 0.5, 7.0, 3.5 };
-            bcf = new BicubicSplineInterpolatingFunction( xval1, yval, zval );
-            Assert.fail( "Failed to detect unsorted x arguments." );
+            };
+        for (int i = 0; i < sz; i++) {
+            for (int j = 0; j < sz; j++) {
+                dFdX[i][j] = dfdX.value(val[i], val[j]);
+            }
+        }
+        // Partial derivatives with respect to y
+        double[][] dFdY = new double[sz][sz];
+        BivariateFunction dfdY = new BivariateFunction() {
+                public double value(double x, double y) {
+                    final double x2 = x * x;
+                    final double y2 = y * y;                    
+                    return 2 - x - 6 * y + 4 * x2 - 2 * x * y + 3 * y2;
+                }
+            };
+        for (int i = 0; i < sz; i++) {
+            for (int j = 0; j < sz; j++) {
+                dFdY[i][j] = dfdY.value(val[i], val[j]);
+            }
+        }
+        // Partial cross-derivatives
+        double[][] d2FdXdY = new double[sz][sz];
+        BivariateFunction d2fdXdY = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return -1 + 8 * x - 2 * y;
+                }
+            };
+        for (int i = 0; i < sz; i++) {
+            for (int j = 0; j < sz; j++) {
+                d2FdXdY[i][j] = d2fdXdY.value(val[i], val[j]);
             }
-        catch ( NonMonotonicSequenceException iae )
-        {
-            // Expected.
         }
 
-        // Y values not sorted.
-        try
-        {
-            double yval1[] = { 0.0, 1.0, 1.5, 0.0, 3.0 };
-            bcf = new BicubicSplineInterpolatingFunction( xval, yval1, zval );
-            Assert.fail( "Failed to detect unsorted y arguments." );
+        BicubicSplineInterpolatingFunction bcf
+            = new BicubicSplineInterpolatingFunction(val, val, fval, dFdX, dFdY, d2FdXdY);
+
+        double x, y;
+        double expected, result;
+
+        final double tol = 1e-12;
+        for (int i = 0; i < sz; i++) {
+            x = val[i];
+            for (int j = 0; j < sz; j++) {
+                y = val[j];
+                
+                expected = dfdX.value(x, y);
+                result = bcf.partialDerivativeX(x, y);
+                Assert.assertEquals(x + " " + y + " dFdX", expected, result, tol);
+
+                expected = dfdY.value(x, y);
+                result = bcf.partialDerivativeY(x, y);
+                Assert.assertEquals(x + " " + y + " dFdY", expected, result, tol);
+                
+                expected = d2fdXdY.value(x, y);
+                result = bcf.partialDerivativeXY(x, y);
+                Assert.assertEquals(x + " " + y + " d2FdXdY", expected, result, tol);
             }
-        catch ( NonMonotonicSequenceException iae )
-        {
-            // Expected.
         }
     }
 
@@ -166,28 +454,73 @@ public final class BicubicSplineInterpolatingFunctionTest
      * z = 2 x - 3 y + 5
      */
     @Test
-    public void testInterpolatePlane()
-    {
-        final int numberOfElements = 10;
-        final double minimumX = -10;
-        final double maximumX = 10;
-        final double minimumY = -10;
-        final double maximumY = 10;
-        final int numberOfSamples = 100;
-        final double interpolationTolerance = 7e-15;
-        final double maxTolerance = 6e-14;
+    public void testInterpolation1() {
+        final int sz = 21;
+        double[] xval = new double[sz];
+        double[] yval = new double[sz];
+        // Coordinate values
+        final double delta = 1d / (sz - 1);
+        for (int i = 0; i < sz; i++) {
+            xval[i] = -1 + 15 * i * delta;
+            yval[i] = -20 + 30 * i * delta;
+        }
 
         // Function values
-        BivariateFunction f = new BivariateFunction()
-        {
-            public double value( double x, double y )
-            {
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
                     return 2 * x - 3 * y + 5;
                 }
             };
+        double[][] zval = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                zval[i][j] = f.value(xval[i], yval[j]);
+            }
+        }
+        // Partial derivatives with respect to x
+        double[][] dZdX = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdX[i][j] = 2;
+            }
+        }
+        // Partial derivatives with respect to y
+        double[][] dZdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdY[i][j] = -3;
+            }
+        }
+        // Partial cross-derivatives
+        double[][] dZdXdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdXdY[i][j] = 0;
+            }
+        }
+
+        final BivariateFunction bcf
+            = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+                                                     dZdX, dZdY, dZdXdY);
+        double x, y;
 
-        testInterpolation( minimumX, maximumX, minimumY, maximumY, numberOfElements, numberOfSamples, f,
-                           interpolationTolerance, maxTolerance );
+        final RandomGenerator rng = new Well19937c(1234567L); // "tol" depends on the seed.
+        final UniformRealDistribution distX
+            = new UniformRealDistribution(rng, xval[0], xval[xval.length - 1]);
+        final UniformRealDistribution distY
+            = new UniformRealDistribution(rng, yval[0], yval[yval.length - 1]);
+
+        final int numSamples = 50;
+        final double tol = 6;
+        for (int i = 0; i < numSamples; i++) {
+            x = distX.sample();
+            for (int j = 0; j < numSamples; j++) {
+                y = distY.sample();
+//                 System.out.println(x + " " + y + " " + f.value(x, y) + " " + bcf.value(x, y));
+                Assert.assertEquals(f.value(x, y),  bcf.value(x, y), tol);
+            }
+//             System.out.println();
+        }
     }
 
     /**
@@ -196,85 +529,140 @@ public final class BicubicSplineInterpolatingFunctionTest
      * z = 2 x<sup>2</sup> - 3 y<sup>2</sup> + 4 x y - 5
      */
     @Test
-    public void testInterpolationParabaloid()
-    {
-        final int numberOfElements = 10;
-        final double minimumX = -10;
-        final double maximumX = 10;
-        final double minimumY = -10;
-        final double maximumY = 10;
-        final int numberOfSamples = 100;
-        final double interpolationTolerance = 2e-14;
-        final double maxTolerance = 6e-14;
+    public void testInterpolation2() {
+        final int sz = 21;
+        double[] xval = new double[sz];
+        double[] yval = new double[sz];
+        // Coordinate values
+        final double delta = 1d / (sz - 1);
+        for (int i = 0; i < sz; i++) {
+            xval[i] = -1 + 15 * i * delta;
+            yval[i] = -20 + 30 * i * delta;
+        }
 
         // Function values
-        BivariateFunction f = new BivariateFunction()
-        {
-            public double value( double x, double y )
-            {
+        BivariateFunction f = new BivariateFunction() {
+                public double value(double x, double y) {
                     return 2 * x * x - 3 * y * y + 4 * x * y - 5;
                 }
             };
-
-        testInterpolation( minimumX, maximumX, minimumY, maximumY, numberOfElements, numberOfSamples, f,
-                           interpolationTolerance, maxTolerance );
-        }
-
-    private void testInterpolation( double minimumX, double maximumX, double minimumY, double maximumY,
-                                    int numberOfElements, int numberOfSamples, BivariateFunction f, double tolerance,
-                                    double maxTolerance )
-    {
-        double expected;
-        double actual;
-        double currentX;
-        double currentY;
-        final double deltaX = ( maximumX - minimumX ) / ( (double) numberOfElements );
-        final double deltaY = ( maximumY - minimumY ) / ( (double) numberOfElements );
-        double xValues[] = new double[numberOfElements];
-        double yValues[] = new double[numberOfElements];
-        double zValues[][] = new double[numberOfElements][numberOfElements];
-
-        for ( int i = 0; i < numberOfElements; i++ )
-        {
-            xValues[i] = minimumX + deltaX * (double) i;
-            for ( int j = 0; j < numberOfElements; j++ )
-            {
-                yValues[j] = minimumY + deltaY * (double) j;
-                zValues[i][j] = f.value( xValues[i], yValues[j] );
+        double[][] zval = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                zval[i][j] = f.value(xval[i], yval[j]);
+            }
+        }
+        // Partial derivatives with respect to x
+        double[][] dZdX = new double[xval.length][yval.length];
+        BivariateFunction dfdX = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * (x + y);
+                }
+            };
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdX[i][j] = dfdX.value(xval[i], yval[j]);
+            }
+        }
+        // Partial derivatives with respect to y
+        double[][] dZdY = new double[xval.length][yval.length];
+        BivariateFunction dfdY = new BivariateFunction() {
+                public double value(double x, double y) {
+                    return 4 * x - 6 * y;
+                }
+            };
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdY[i][j] = dfdY.value(xval[i], yval[j]);
+            }
+        }
+        // Partial cross-derivatives
+        double[][] dZdXdY = new double[xval.length][yval.length];
+        for (int i = 0; i < xval.length; i++) {
+            for (int j = 0; j < yval.length; j++) {
+                dZdXdY[i][j] = 4;
             }
         }
 
-        BivariateFunction interpolation = new BicubicSplineInterpolatingFunction( xValues, yValues, zValues );
+        BivariateFunction bcf = new BicubicSplineInterpolatingFunction(xval, yval, zval,
+                                                                       dZdX, dZdY, dZdXdY);
+        double x, y;
 
-        for ( int i = 0; i < numberOfElements; i++ )
-        {
-            currentX = xValues[i];
-            for ( int j = 0; j < numberOfElements; j++ )
-            {
-                currentY = yValues[j];
-                expected = f.value( currentX, currentY );
-                actual = interpolation.value( currentX, currentY );
-                assertTrue( Precision.equals( expected, actual ) );
+        final RandomGenerator rng = new Well19937c(1234567L); // "tol" depends on the seed.
+        final UniformRealDistribution distX
+            = new UniformRealDistribution(rng, xval[0], xval[xval.length - 1]);
+        final UniformRealDistribution distY
+            = new UniformRealDistribution(rng, yval[0], yval[yval.length - 1]);
+
+        final double tol = 224;
+        for (int i = 0; i < sz; i++) {
+            x = distX.sample();
+            for (int j = 0; j < sz; j++) {
+                y = distY.sample();
+//                 System.out.println(x + " " + y + " " + f.value(x, y) + " " + bcf.value(x, y));
+                Assert.assertEquals(f.value(x, y),  bcf.value(x, y), tol);
             }
+//             System.out.println();
         }
-
-        final RandomGenerator rng = new Well19937c( 1234567L ); // "tol" depends on the seed.
-        final UniformRealDistribution distX =
-            new UniformRealDistribution( rng, xValues[0], xValues[xValues.length - 1] );
-        final UniformRealDistribution distY =
-            new UniformRealDistribution( rng, yValues[0], yValues[yValues.length - 1] );
-
-        double sumError = 0;
-        for ( int i = 0; i < numberOfSamples; i++ )
-        {
-            currentX = distX.sample();
-            currentY = distY.sample();
-            expected = f.value( currentX, currentY );
-            actual = interpolation.value( currentX, currentY );
-            sumError += FastMath.abs( actual - expected );
-            assertEquals( expected, actual, maxTolerance );
     }
 
-        assertEquals( 0.0, ( sumError / (double) numberOfSamples ), tolerance );
+    @Test
+    public void testIsValidPoint() {
+        final double xMin = -12;
+        final double xMax = 34;
+        final double yMin = 5;
+        final double yMax = 67;
+        final double[] xval = new double[] { xMin, xMax };
+        final double[] yval = new double[] { yMin, yMax };
+        final double[][] f = new double[][] { { 1, 2 },
+                                              { 3, 4 } };
+        final double[][] dFdX = f;
+        final double[][] dFdY = f;
+        final double[][] dFdXdY = f;
+
+        final BicubicSplineInterpolatingFunction bcf
+            = new BicubicSplineInterpolatingFunction(xval, yval, f,
+                                                     dFdX, dFdY, dFdXdY);
+
+        double x, y;
+
+        x = xMin;
+        y = yMin;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+
+        x = xMax;
+        y = yMax;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+ 
+        final double xRange = xMax - xMin;
+        final double yRange = yMax - yMin;
+        x = xMin + xRange / 3.4;
+        y = yMin + yRange / 1.2;
+        Assert.assertTrue(bcf.isValidPoint(x, y));
+        // Ensure that no exception is thrown.
+        bcf.value(x, y);
+
+        final double small = 1e-8;
+        x = xMin - small;
+        y = yMax;
+        Assert.assertFalse(bcf.isValidPoint(x, y));
+        // Ensure that an exception would have been thrown.
+        try {
+            bcf.value(x, y);
+            Assert.fail("OutOfRangeException expected");
+        } catch (OutOfRangeException expected) {}
+
+        x = xMin;
+        y = yMax + small;
+        Assert.assertFalse(bcf.isValidPoint(x, y));
+        // Ensure that an exception would have been thrown.
+        try {
+            bcf.value(x, y);
+            Assert.fail("OutOfRangeException expected");
+        } catch (OutOfRangeException expected) {}
     }
 }


Mime
View raw message