You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2014/12/03 12:31:55 UTC

[2/3] [math] MATH-1173: Tricubic interpolation

MATH-1173: Tricubic interpolation

New class "TricubicInterpolatingFunction" to replace "TricubicSplineInterpolatingFunction".


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

Branch: refs/heads/master
Commit: fd7a4362caf2cd69368a38f2f71ffe19706e341c
Parents: 17f52a2
Author: Gilles <er...@apache.org>
Authored: Wed Dec 3 12:25:09 2014 +0100
Committer: Gilles <er...@apache.org>
Committed: Wed Dec 3 12:25:09 2014 +0100

----------------------------------------------------------------------
 .../TricubicInterpolatingFunction.java          | 510 +++++++++++++++
 .../TricubicInterpolatingFunctionTest.java      | 631 +++++++++++++++++++
 2 files changed, 1141 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/fd7a4362/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java
new file mode 100644
index 0000000..b527996
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java
@@ -0,0 +1,510 @@
+/*
+ * 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.analysis.TrivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+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://en.wikipedia.org/wiki/Tricubic_interpolation">
+ * tricubic spline interpolation</a>, as proposed in
+ * <quote>
+ *  Tricubic interpolation in three dimensions<br/>
+ *  F. Lekien and J. Marsden<br/>
+ *  <em>Int. J. Numer. Meth. Eng</em> 2005; <b>63</b>:455-471
+ * </quote>
+ *
+ * @since 2.2
+ * @deprecated in 3.4.
+ */
+@Deprecated
+public class TricubicInterpolatingFunction
+    implements TrivariateFunction {
+    /**
+     * 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 9,-9,-9,9,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,0,0,0,0,0,0,0,0,4,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -6,6,6,-6,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -6,6,6,-6,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 4,-4,-4,4,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,-9,9,0,0,0,0,0,0,0,0,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,4,2,2,1,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,-2,-2,-1,-1,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,-2,-1,-2,-1,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,-4,4,0,0,0,0,0,0,0,0,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,1,1,1,1,0,0,0,0 },
+        {-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 9,-9,0,0,-9,9,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,0,0,0,0,0,0,0,0,4,2,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -6,6,0,0,6,-6,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,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 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,0,0,-9,9,0,0,0,0,0,0,0,0,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,4,2,0,0,2,1,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,-2,-2,0,0,-1,-1,0,0 },
+        { 9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0 },
+        { -27,27,27,-27,27,-27,-27,27,-18,-9,18,9,18,9,-18,-9,-18,18,-9,9,18,-18,9,-9,-18,18,18,-18,-9,9,9,-9,-12,-6,-6,-3,12,6,6,3,-12,-6,12,6,-6,-3,6,3,-12,12,-6,6,-6,6,-3,3,-8,-4,-4,-2,-4,-2,-2,-1 },
+        { 18,-18,-18,18,-18,18,18,-18,9,9,-9,-9,-9,-9,9,9,12,-12,6,-6,-12,12,-6,6,12,-12,-12,12,6,-6,-6,6,6,6,3,3,-6,-6,-3,-3,6,6,-6,-6,3,3,-3,-3,8,-8,4,-4,4,-4,2,-2,4,4,2,2,2,2,1,1 },
+        { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0 },
+        { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,9,-9,9,-9,-9,9,-9,9,12,-12,-12,12,6,-6,-6,6,6,3,6,3,-6,-3,-6,-3,8,4,-8,-4,4,2,-4,-2,6,-6,6,-6,3,-3,3,-3,4,2,4,2,2,1,2,1 },
+        { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-6,6,-6,6,6,-6,6,-6,-8,8,8,-8,-4,4,4,-4,-3,-3,-3,-3,3,3,3,3,-4,-4,4,4,-2,-2,2,2,-4,4,-4,4,-2,2,-2,2,-2,-2,-2,-2,-1,-1,-1,-1 },
+        { 2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { -6,6,0,0,6,-6,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 4,-4,0,0,-4,4,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,-2,-1,0,0,-2,-1,0,0 },
+        { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,0,0,-4,4,0,0,0,0,0,0,0,0,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,1,1,0,0,1,1,0,0 },
+        { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0 },
+        { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,12,-12,6,-6,-12,12,-6,6,9,-9,-9,9,9,-9,-9,9,8,4,4,2,-8,-4,-4,-2,6,3,-6,-3,6,3,-6,-3,6,-6,3,-3,6,-6,3,-3,4,2,2,1,4,2,2,1 },
+        { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-8,8,-4,4,8,-8,4,-4,-6,6,6,-6,-6,6,6,-6,-4,-4,-2,-2,4,4,2,2,-3,-3,3,3,-3,-3,3,3,-4,4,-2,2,-4,4,-2,2,-2,-2,-1,-1,-2,-2,-1,-1 },
+        { 4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0 },
+        { 0,0,0,0,0,0,0,0,4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0 },
+        { -12,12,12,-12,12,-12,-12,12,-8,-4,8,4,8,4,-8,-4,-6,6,-6,6,6,-6,6,-6,-6,6,6,-6,-6,6,6,-6,-4,-2,-4,-2,4,2,4,2,-4,-2,4,2,-4,-2,4,2,-3,3,-3,3,-3,3,-3,3,-2,-1,-2,-1,-2,-1,-2,-1 },
+        { 8,-8,-8,8,-8,8,8,-8,4,4,-4,-4,-4,-4,4,4,4,-4,4,-4,-4,4,-4,4,4,-4,-4,4,4,-4,-4,4,2,2,2,2,-2,-2,-2,-2,2,2,-2,-2,2,2,-2,-2,2,-2,2,-2,2,-2,2,-2,1,1,1,1,1,1,1,1 }
+    };
+
+    /** Samples x-coordinates */
+    private final double[] xval;
+    /** Samples y-coordinates */
+    private final double[] yval;
+    /** Samples z-coordinates */
+    private final double[] zval;
+    /** Set of cubic splines pacthing the whole data grid */
+    private final TricubicFunction[][][] splines;
+
+    /**
+     * @param x Sample values of the x-coordinate, in increasing order.
+     * @param y Sample values of the y-coordinate, in increasing order.
+     * @param z 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 dFdZ Values of the partial derivative of function with respect to z on every grid point.
+     * @param d2FdXdY Values of the cross partial derivative of function on every grid point.
+     * @param d2FdXdZ Values of the cross partial derivative of function on every grid point.
+     * @param d2FdYdZ Values of the cross partial derivative of function on every grid point.
+     * @param d3FdXdYdZ Values of the cross partial derivative of function on every grid point.
+     * @throws NoDataException if any of the arrays has zero length.
+     * @throws DimensionMismatchException if the various arrays do not contain the expected number of elements.
+     * @throws NonMonotonicSequenceException if {@code x}, {@code y} or {@code z} are not strictly increasing.
+     */
+    public TricubicInterpolatingFunction(double[] x,
+                                         double[] y,
+                                         double[] z,
+                                         double[][][] f,
+                                         double[][][] dFdX,
+                                         double[][][] dFdY,
+                                         double[][][] dFdZ,
+                                         double[][][] d2FdXdY,
+                                         double[][][] d2FdXdZ,
+                                         double[][][] d2FdYdZ,
+                                         double[][][] d3FdXdYdZ)
+        throws NoDataException,
+               DimensionMismatchException,
+               NonMonotonicSequenceException {
+        final int xLen = x.length;
+        final int yLen = y.length;
+        final int zLen = z.length;
+
+        if (xLen == 0 || yLen == 0 || z.length == 0 || f.length == 0 || f[0].length == 0) {
+            throw new NoDataException();
+        }
+        if (xLen != f.length) {
+            throw new DimensionMismatchException(xLen, f.length);
+        }
+        if (xLen != dFdX.length) {
+            throw new DimensionMismatchException(xLen, dFdX.length);
+        }
+        if (xLen != dFdY.length) {
+            throw new DimensionMismatchException(xLen, dFdY.length);
+        }
+        if (xLen != dFdZ.length) {
+            throw new DimensionMismatchException(xLen, dFdZ.length);
+        }
+        if (xLen != d2FdXdY.length) {
+            throw new DimensionMismatchException(xLen, d2FdXdY.length);
+        }
+        if (xLen != d2FdXdZ.length) {
+            throw new DimensionMismatchException(xLen, d2FdXdZ.length);
+        }
+        if (xLen != d2FdYdZ.length) {
+            throw new DimensionMismatchException(xLen, d2FdYdZ.length);
+        }
+        if (xLen != d3FdXdYdZ.length) {
+            throw new DimensionMismatchException(xLen, d3FdXdYdZ.length);
+        }
+
+        MathArrays.checkOrder(x);
+        MathArrays.checkOrder(y);
+        MathArrays.checkOrder(z);
+
+        xval = x.clone();
+        yval = y.clone();
+        zval = z.clone();
+
+        final int lastI = xLen - 1;
+        final int lastJ = yLen - 1;
+        final int lastK = zLen - 1;
+        splines = new TricubicFunction[lastI][lastJ][lastK];
+
+        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 (dFdZ[i].length != yLen) {
+                throw new DimensionMismatchException(dFdZ[i].length, yLen);
+            }
+            if (d2FdXdY[i].length != yLen) {
+                throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+            }
+            if (d2FdXdZ[i].length != yLen) {
+                throw new DimensionMismatchException(d2FdXdZ[i].length, yLen);
+            }
+            if (d2FdYdZ[i].length != yLen) {
+                throw new DimensionMismatchException(d2FdYdZ[i].length, yLen);
+            }
+            if (d3FdXdYdZ[i].length != yLen) {
+                throw new DimensionMismatchException(d3FdXdYdZ[i].length, yLen);
+            }
+
+            final int ip1 = i + 1;
+            final double xR = xval[ip1] - xval[i];
+            for (int j = 0; j < lastJ; j++) {
+                if (f[i][j].length != zLen) {
+                    throw new DimensionMismatchException(f[i][j].length, zLen);
+                }
+                if (dFdX[i][j].length != zLen) {
+                    throw new DimensionMismatchException(dFdX[i][j].length, zLen);
+                }
+                if (dFdY[i][j].length != zLen) {
+                    throw new DimensionMismatchException(dFdY[i][j].length, zLen);
+                }
+                if (dFdZ[i][j].length != zLen) {
+                    throw new DimensionMismatchException(dFdZ[i][j].length, zLen);
+                }
+                if (d2FdXdY[i][j].length != zLen) {
+                    throw new DimensionMismatchException(d2FdXdY[i][j].length, zLen);
+                }
+                if (d2FdXdZ[i][j].length != zLen) {
+                    throw new DimensionMismatchException(d2FdXdZ[i][j].length, zLen);
+                }
+                if (d2FdYdZ[i][j].length != zLen) {
+                    throw new DimensionMismatchException(d2FdYdZ[i][j].length, zLen);
+                }
+                if (d3FdXdYdZ[i][j].length != zLen) {
+                    throw new DimensionMismatchException(d3FdXdYdZ[i][j].length, zLen);
+                }
+
+                final int jp1 = j + 1;
+                final double yR = yval[jp1] - yval[j];
+                final double xRyR = xR * yR;
+                for (int k = 0; k < lastK; k++) {
+                    final int kp1 = k + 1;
+                    final double zR = zval[kp1] - zval[k];
+                    final double xRzR = xR * zR;
+                    final double yRzR = yR * zR;
+                    final double xRyRzR = xR * yRzR;
+
+                    final double[] beta = new double[] {
+                        f[i][j][k], f[ip1][j][k],
+                        f[i][jp1][k], f[ip1][jp1][k],
+                        f[i][j][kp1], f[ip1][j][kp1],
+                        f[i][jp1][kp1], f[ip1][jp1][kp1],
+
+                        dFdX[i][j][k] * xR, dFdX[ip1][j][k] * xR,
+                        dFdX[i][jp1][k] * xR, dFdX[ip1][jp1][k] * xR,
+                        dFdX[i][j][kp1] * xR, dFdX[ip1][j][kp1] * xR,
+                        dFdX[i][jp1][kp1] * xR, dFdX[ip1][jp1][kp1] * xR,
+
+                        dFdY[i][j][k] * yR, dFdY[ip1][j][k] * yR,
+                        dFdY[i][jp1][k] * yR, dFdY[ip1][jp1][k] * yR,
+                        dFdY[i][j][kp1] * yR, dFdY[ip1][j][kp1] * yR,
+                        dFdY[i][jp1][kp1] * yR, dFdY[ip1][jp1][kp1] * yR,
+
+                        dFdZ[i][j][k] * zR, dFdZ[ip1][j][k] * zR,
+                        dFdZ[i][jp1][k] * zR, dFdZ[ip1][jp1][k] * zR,
+                        dFdZ[i][j][kp1] * zR, dFdZ[ip1][j][kp1] * zR,
+                        dFdZ[i][jp1][kp1] * zR, dFdZ[ip1][jp1][kp1] * zR,
+
+                        d2FdXdY[i][j][k] * xRyR, d2FdXdY[ip1][j][k] * xRyR,
+                        d2FdXdY[i][jp1][k] * xRyR, d2FdXdY[ip1][jp1][k] * xRyR,
+                        d2FdXdY[i][j][kp1] * xRyR, d2FdXdY[ip1][j][kp1] * xRyR,
+                        d2FdXdY[i][jp1][kp1] * xRyR, d2FdXdY[ip1][jp1][kp1] * xRyR,
+
+                        d2FdXdZ[i][j][k] * xRzR, d2FdXdZ[ip1][j][k] * xRzR,
+                        d2FdXdZ[i][jp1][k] * xRzR, d2FdXdZ[ip1][jp1][k] * xRzR,
+                        d2FdXdZ[i][j][kp1] * xRzR, d2FdXdZ[ip1][j][kp1] * xRzR,
+                        d2FdXdZ[i][jp1][kp1] * xRzR, d2FdXdZ[ip1][jp1][kp1] * xRzR,
+
+                        d2FdYdZ[i][j][k] * yRzR, d2FdYdZ[ip1][j][k] * yRzR,
+                        d2FdYdZ[i][jp1][k] * yRzR, d2FdYdZ[ip1][jp1][k] * yRzR,
+                        d2FdYdZ[i][j][kp1] * yRzR, d2FdYdZ[ip1][j][kp1] * yRzR,
+                        d2FdYdZ[i][jp1][kp1] * yRzR, d2FdYdZ[ip1][jp1][kp1] * yRzR,
+
+                        d3FdXdYdZ[i][j][k] * xRyRzR, d3FdXdYdZ[ip1][j][k] * xRyRzR,
+                        d3FdXdYdZ[i][jp1][k] * xRyRzR, d3FdXdYdZ[ip1][jp1][k] * xRyRzR,
+                        d3FdXdYdZ[i][j][kp1] * xRyRzR, d3FdXdYdZ[ip1][j][kp1] * xRyRzR,
+                        d3FdXdYdZ[i][jp1][kp1] * xRyRzR, d3FdXdYdZ[ip1][jp1][kp1] * xRyRzR,
+                    };
+
+                    splines[i][j][k] = new TricubicFunction(computeCoefficients(beta));
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws OutOfRangeException if any of the variables is outside its interpolation range.
+     */
+    public double value(double x, double y, double z)
+        throws OutOfRangeException {
+        final int i = searchIndex(x, xval);
+        if (i == -1) {
+            throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]);
+        }
+        final int j = searchIndex(y, yval);
+        if (j == -1) {
+            throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]);
+        }
+        final int k = searchIndex(z, zval);
+        if (k == -1) {
+            throw new OutOfRangeException(z, zval[0], zval[zval.length - 1]);
+        }
+
+        final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+        final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+        final double zN = (z - zval[k]) / (zval[k + 1] - zval[k]);
+
+        return splines[i][j][k].value(xN, yN, zN);
+    }
+
+    /**
+     * Indicates whether a point is within the interpolation range.
+     *
+     * @param x First coordinate.
+     * @param y Second coordinate.
+     * @param z Third coordinate.
+     * @return {@code true} if (x, y, z) is a valid point.
+     */
+    public boolean isValidPoint(double x, double y, double z) {
+        if (x < xval[0] ||
+            x > xval[xval.length - 1] ||
+            y < yval[0] ||
+            y > yval[yval.length - 1] ||
+            z < zval[0] ||
+            z > zval[zval.length - 1]) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * @param c Coordinate.
+     * @param val Coordinate samples.
+     * @return the index in {@code val} corresponding to the interval containing {@code c}, or {@code -1}
+     *   if {@code c} is out of the range defined by the end values of {@code val}.
+     */
+    private int searchIndex(double c, double[] val) {
+        if (c < val[0]) {
+            return -1;
+        }
+
+        final int max = val.length;
+        for (int i = 1; i < max; i++) {
+            if (c <= val[i]) {
+                return i - 1;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * 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,0)</li>
+     *  <li>f(1,0,0)</li>
+     *  <li>f(0,1,0)</li>
+     *  <li>f(1,1,0)</li>
+     *  <li>f(0,0,1)</li>
+     *  <li>f(1,0,1)</li>
+     *  <li>f(0,1,1)</li>
+     *  <li>f(1,1,1)</li>
+     *
+     *  <li>f<sub>x</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>x</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>y</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>y</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>z</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>z</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>xy</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>xy</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>xz</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>xz</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>yz</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>yz</sub>(1,1,1)</li>
+     *
+     *  <li>f<sub>xyz</sub>(0,0,0)</li>
+     *  <li>... <em>(same order as above)</em></li>
+     *  <li>f<sub>xyz</sub>(1,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[] computeCoefficients(double[] beta) {
+        final int sz = 64;
+        final double[] a = new double[sz];
+
+        for (int i = 0; i < sz; i++) {
+            double result = 0;
+            final double[] row = AINV[i];
+            for (int j = 0; j < sz; j++) {
+                result += row[j] * beta[j];
+            }
+            a[i] = result;
+        }
+
+        return a;
+    }
+}
+
+/**
+ * 3D-spline function.
+ *
+ */
+class TricubicFunction
+    implements TrivariateFunction {
+    /** Number of points. */
+    private static final short N = 4;
+    /** Coefficients */
+    private final double[][][] a = new double[N][N][N];
+
+    /**
+     * @param aV List of spline coefficients.
+     */
+    public TricubicFunction(double[] aV) {
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < N; j++) {
+                for (int k = 0; k < N; k++) {
+                    a[i][j][k] = aV[i + N * (j + N * k)];
+                }
+            }
+        }
+    }
+
+    /**
+     * @param x x-coordinate of the interpolation point.
+     * @param y y-coordinate of the interpolation point.
+     * @param z z-coordinate of the interpolation point.
+     * @return the interpolated value.
+     * @throws OutOfRangeException if {@code x}, {@code y} or
+     * {@code z} are not in the interval {@code [0, 1]}.
+     */
+    public double value(double x, double y, double z)
+        throws OutOfRangeException {
+        if (x < 0 || x > 1) {
+            throw new OutOfRangeException(x, 0, 1);
+        }
+        if (y < 0 || y > 1) {
+            throw new OutOfRangeException(y, 0, 1);
+        }
+        if (z < 0 || z > 1) {
+            throw new OutOfRangeException(z, 0, 1);
+        }
+
+        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 };
+
+        final double z2 = z * z;
+        final double z3 = z2 * z;
+        final double[] pZ = { 1, z, z2, z3 };
+
+        double result = 0;
+        for (int i = 0; i < N; i++) {
+            for (int j = 0; j < N; j++) {
+                for (int k = 0; k < N; k++) {
+                    result += a[i][j][k] * pX[i] * pY[j] * pZ[k];
+                }
+            }
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/fd7a4362/src/test/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunctionTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunctionTest.java b/src/test/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunctionTest.java
new file mode 100644
index 0000000..9760555
--- /dev/null
+++ b/src/test/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunctionTest.java
@@ -0,0 +1,631 @@
+/*
+ * 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.MathIllegalArgumentException;
+import org.apache.commons.math3.analysis.TrivariateFunction;
+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;
+
+/**
+ * Test case for the bicubic function.
+ * 
+ */
+public final class TricubicInterpolatingFunctionTest {
+    /**
+     * Test preconditions.
+     */
+    @Test
+    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[] {-12, -8, -5.5, -3, 0, 2.5};
+        double[][][] fval = new double[xval.length][yval.length][zval.length];
+
+        @SuppressWarnings("unused")
+        TrivariateFunction tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                                   fval, fval, fval, fval,
+                                                                   fval, fval, fval, fval);
+        
+        double[] wxval = new double[] {3, 2, 5, 6.5};
+        try {
+            tcf = new TricubicInterpolatingFunction(wxval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[] wyval = new double[] {-4, -1, -1, 2.5};
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, wyval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[] wzval = new double[] {-12, -8, -9, -3, 0, 2.5};
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, wzval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (MathIllegalArgumentException e) {
+            // Expected
+        }
+        double[][][] wfval = new double[xval.length - 1][yval.length - 1][zval.length];
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    wfval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, wfval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, wfval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, wfval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    wfval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, wfval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, wfval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, wfval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        wfval = new double[xval.length][yval.length - 1][zval.length];
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    wfval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, wfval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, wfval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, wfval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    wfval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, wfval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, wfval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, wfval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        wfval = new double[xval.length][yval.length][zval.length - 1];
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    wfval, fval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, wfval, fval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, wfval, fval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, wfval,
+                                                    fval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    wfval, fval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, wfval, fval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, wfval, fval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+        try {
+            tcf = new TricubicInterpolatingFunction(xval, yval, zval,
+                                                    fval, fval, fval, fval,
+                                                    fval, fval, fval, wfval);
+            Assert.fail("an exception should have been thrown");
+        } catch (DimensionMismatchException e) {
+            // Expected
+        }
+    }
+
+    /**
+     * @param minimumX Lower bound of interpolation range along the x-coordinate.
+     * @param maximumX Higher bound of interpolation range along the x-coordinate.
+     * @param minimumY Lower bound of interpolation range along the y-coordinate.
+     * @param maximumY Higher bound of interpolation range along the y-coordinate.
+     * @param minimumZ Lower bound of interpolation range along the z-coordinate.
+     * @param maximumZ Higher bound of interpolation range along the z-coordinate.
+     * @param numberOfElements Number of data points (along each dimension).
+     * @param numberOfSamples Number of test points.
+     * @param f Function to test.
+     * @param dfdx Partial derivative w.r.t. x of the function to test.
+     * @param dfdy Partial derivative w.r.t. y of the function to test.
+     * @param dfdz Partial derivative w.r.t. z of the function to test.
+     * @param d2fdxdy Second partial cross-derivative w.r.t x and y of the function to test.
+     * @param d2fdxdz Second partial cross-derivative w.r.t x and z of the function to test.
+     * @param d2fdydz Second partial cross-derivative w.r.t y and z of the function to test.
+     * @param d3fdxdy Third partial cross-derivative w.r.t x, y and z of the function to test.
+     * @param meanRelativeTolerance Allowed average error (mean error on all interpolated values).
+     * @param maxRelativeTolerance Allowed error on each interpolated value.
+     * @param onDataMaxAbsoluteTolerance Allowed error on a data point.
+     */
+    private void testInterpolation(double minimumX,
+                                   double maximumX,
+                                   double minimumY,
+                                   double maximumY,
+                                   double minimumZ,
+                                   double maximumZ,
+                                   int numberOfElements,
+                                   int numberOfSamples,
+                                   TrivariateFunction f,
+                                   TrivariateFunction dfdx,
+                                   TrivariateFunction dfdy,
+                                   TrivariateFunction dfdz,
+                                   TrivariateFunction d2fdxdy,
+                                   TrivariateFunction d2fdxdz,
+                                   TrivariateFunction d2fdydz,
+                                   TrivariateFunction d3fdxdydz,
+                                   double meanRelativeTolerance,
+                                   double maxRelativeTolerance,
+                                   double onDataMaxAbsoluteTolerance,
+                                   boolean print) {
+        double expected;
+        double actual;
+        double currentX;
+        double currentY;
+        double currentZ;
+        final double deltaX = (maximumX - minimumX) / numberOfElements;
+        final double deltaY = (maximumY - minimumY) / numberOfElements;
+        final double deltaZ = (maximumZ - minimumZ) / numberOfElements;
+        final double[] xValues = new double[numberOfElements];
+        final double[] yValues = new double[numberOfElements];
+        final double[] zValues = new double[numberOfElements];
+        final double[][][] fValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] dfdxValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] dfdyValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] dfdzValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] d2fdxdyValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] d2fdxdzValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] d2fdydzValues = new double[numberOfElements][numberOfElements][numberOfElements];
+        final double[][][] d3fdxdydzValues = new double[numberOfElements][numberOfElements][numberOfElements];
+
+        for (int i = 0; i < numberOfElements; i++) {
+            xValues[i] = minimumX + deltaX * i;
+            final double x = xValues[i];
+            for (int j = 0; j < numberOfElements; j++) {
+                yValues[j] = minimumY + deltaY * j;
+                final double y = yValues[j];
+                for (int k = 0; k < numberOfElements; k++) {
+                    zValues[k] = minimumZ + deltaZ * k;
+                    final double z = zValues[k];
+                    fValues[i][j][k] = f.value(x, y, z);
+                    dfdxValues[i][j][k] = dfdx.value(x, y, z);
+                    dfdyValues[i][j][k] = dfdy.value(x, y, z);
+                    dfdzValues[i][j][k] = dfdz.value(x, y, z);
+                    d2fdxdyValues[i][j][k] = d2fdxdy.value(x, y, z);
+                    d2fdxdzValues[i][j][k] = d2fdxdz.value(x, y, z);
+                    d2fdydzValues[i][j][k] = d2fdydz.value(x, y, z);
+                    d3fdxdydzValues[i][j][k] = d3fdxdydz.value(x, y, z);
+                }
+            }
+        }
+
+        final TrivariateFunction interpolation
+            = new TricubicInterpolatingFunction(xValues,
+                                                yValues,
+                                                zValues,
+                                                fValues,
+                                                dfdxValues,
+                                                dfdyValues,
+                                                dfdzValues,
+                                                d2fdxdyValues,
+                                                d2fdxdzValues,
+                                                d2fdydzValues,
+                                                d3fdxdydzValues);
+
+        for (int i = 0; i < numberOfElements; i++) {
+            currentX = xValues[i];
+            for (int j = 0; j < numberOfElements; j++) {
+                currentY = yValues[j];
+                for (int k = 0; k < numberOfElements; k++) {
+                    currentZ = zValues[k];
+                    expected = f.value(currentX, currentY, currentZ);
+                    actual = interpolation.value(currentX, currentY, currentZ);
+                    Assert.assertTrue("On data point: " + expected + " != " + actual,
+                                      Precision.equals(expected, actual, onDataMaxAbsoluteTolerance));
+                }
+            }
+        }
+
+        final RandomGenerator rng = new Well19937c(1234567L);
+        final UniformRealDistribution distX = new UniformRealDistribution(rng, xValues[0], xValues[xValues.length - 1]);
+        final UniformRealDistribution distY = new UniformRealDistribution(rng, yValues[0], yValues[yValues.length - 1]);
+        final UniformRealDistribution distZ = new UniformRealDistribution(rng, zValues[0], zValues[zValues.length - 1]);
+
+        double sumError = 0;
+        for (int i = 0; i < numberOfSamples; i++) {
+            currentX = distX.sample();
+            currentY = distY.sample();
+            currentZ = distZ.sample();
+            expected = f.value(currentX, currentY, currentZ);
+
+            actual = interpolation.value(currentX, currentY, currentZ);
+            final double relativeError = FastMath.abs(actual - expected) / FastMath.max(FastMath.abs(actual), FastMath.abs(expected));
+            sumError += relativeError;
+
+            if (print) {
+                System.out.println(currentX + " " + currentY + " " + currentZ
+                                   + " " + actual
+                                   + " " + expected
+                                   + " " + (expected - actual));
+            }
+
+            Assert.assertEquals(0, relativeError, maxRelativeTolerance);
+        }
+
+        final double meanError = sumError / numberOfSamples;
+        Assert.assertEquals(0, meanError, meanRelativeTolerance);
+    }
+
+    /**
+     * Test for a plane.
+     * <p>
+     *  f(x, y, z) = 2 x - 3 y - 4 z + 5
+     * </p>
+     */
+    @Test
+    public void testPlane() {
+        final TrivariateFunction f = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 2 * x - 3 * y - 4 * z + 5;
+                }
+            };
+
+        final TrivariateFunction dfdx = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 2;
+                }
+            };
+
+        final TrivariateFunction dfdy = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -3;
+                }
+            };
+
+        final TrivariateFunction dfdz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -4;
+                }
+            };
+
+        final TrivariateFunction zero = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 0;
+                }
+            };
+
+        testInterpolation(-10, 3,
+                          4.5, 6,
+                          -150, -117,
+                          7,
+                          1000,
+                          f,
+                          dfdx,
+                          dfdy,
+                          dfdz,
+                          zero,
+                          zero,
+                          zero,
+                          zero,
+                          1e-12,
+                          1e-11,
+                          1e-10,
+                          false);
+    }
+
+    /**
+     * Test for a quadric.
+     * <p>
+     *  f(x, y, z) = 2 x<sup>2</sup> - 3 y<sup>2</sup> - 4 z<sup>2</sup> + 5 x y + 6 x z - 2 y z + 3
+     * </p>
+     */
+    @Test
+    public void testQuadric() {
+        final TrivariateFunction f = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 2 * x * x - 3 * y * y - 4 * z * z + 5 * x * y + 6 * x * z - 2 * y * z + 3;
+                }
+            };
+
+        final TrivariateFunction dfdx = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 4 * x + 5 * y + 6 * z;
+                }
+            };
+
+        final TrivariateFunction dfdy = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -6 * y + 5 * x - 2 * z;
+                }
+            };
+
+        final TrivariateFunction dfdz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -8 * z + 6 * x - 2 * y;
+                }
+            };
+
+        final TrivariateFunction d2fdxdy = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 5;
+                }
+            };
+
+        final TrivariateFunction d2fdxdz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 6;
+                }
+            };
+
+        final TrivariateFunction d2fdydz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -2;
+                }
+            };
+
+        final TrivariateFunction d3fdxdydz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return 0;
+                }
+            };
+
+        testInterpolation(-10, 3,
+                          4.5, 6,
+                          -150, -117,
+                          7,
+                          5000,
+                          f,
+                          dfdx,
+                          dfdy,
+                          dfdz,
+                          d2fdxdy,
+                          d2fdxdz,
+                          d2fdydz,
+                          d3fdxdydz,
+                          1e-12,
+                          1e-11,
+                          1e-8,
+                          false);
+    }
+
+    /**
+     * Wave.
+     * <p>
+     *  f(x, y, z) = a cos (&omega; z - k<sub>x</sub> x - k<sub>y</sub> y)
+     * </p>
+     * with a = 5, &omega; = 0.3, k<sub>x</sub> = 0.8, k<sub>y</sub> = 1.
+     */
+    @Test
+    public void testWave() {
+        final double a = 5;
+        final double omega = 0.3;
+        final double kx = 0.8;
+        final double ky = 1;
+
+        final TrivariateFunction arg = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return omega * z - kx * x - ky * y;
+                }
+            };
+
+        final TrivariateFunction f = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return a * FastMath.cos(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction dfdx = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return kx * a * FastMath.sin(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction dfdy = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return ky * a * FastMath.sin(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction dfdz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -omega * a * FastMath.sin(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction d2fdxdy = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return -ky * kx * a * FastMath.cos(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction d2fdxdz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return omega * kx * a * FastMath.cos(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction d2fdydz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return omega * ky * a * FastMath.cos(arg.value(x, y, z));
+                }
+            };
+
+        final TrivariateFunction d3fdxdydz = new TrivariateFunction() {
+                public double value(double x, double y, double z) {
+                    return omega * ky * kx * a * FastMath.sin(arg.value(x, y, z));
+                }
+            };
+
+        testInterpolation(-10, 3,
+                          4.5, 6,
+                          -150, -117,
+                          30,
+                          5000,
+                          f,
+                          dfdx,
+                          dfdy,
+                          dfdz,
+                          d2fdxdy,
+                          d2fdxdz,
+                          d2fdydz,
+                          d3fdxdydz,
+                          1e-3,
+                          1e-2,
+                          1e-12,
+                          false);
+    }
+}