class ScalarFieldFirstOrderDifferentiator implements Differentiator<DoubleMatrix1D, Double, DoubleMatrix1D> {

  @Override
  public Function1D<DoubleMatrix1D, DoubleMatrix1D> differentiate(final Function1D<DoubleMatrix1D, Double> function) {
    Validate.notNull(function);
    switch (_differenceType) {
      case FORWARD:
        return new Function1D<DoubleMatrix1D, DoubleMatrix1D>() {

          @SuppressWarnings("synthetic-access")
          @Override
          public DoubleMatrix1D evaluate(final DoubleMatrix1D x) {
            Validate.notNull(x, "x");
            final int n = x.getNumberOfElements();
            final double y = function.evaluate(x);
            final double[] xData = x.getData();
            double oldValue;
            final double[] res = new double[n];
            for (int i = 0; i < n; i++) {
              oldValue = xData[i];
              xData[i] += _eps;
              res[i] = (function.evaluate(x) - y) / _eps;
              xData[i] = oldValue;
            }
            return new DoubleMatrix1D(res);
          }
        };
      case CENTRAL:
        return new Function1D<DoubleMatrix1D, DoubleMatrix1D>() {

          @SuppressWarnings("synthetic-access")
          @Override
          public DoubleMatrix1D evaluate(final DoubleMatrix1D x) {
            Validate.notNull(x, "x");
            final int n = x.getNumberOfElements();
            final double[] xData = x.getData();
            double oldValue;
            double up, down;
            final double[] res = new double[n];
            for (int i = 0; i < n; i++) {
              oldValue = xData[i];
              xData[i] += _eps;
              up = function.evaluate(x);
              xData[i] -= _twoEps;
              down = function.evaluate(x);
              res[i] = (up - down) / _twoEps;
              xData[i] = oldValue;
            }
            return new DoubleMatrix1D(res);
          }
        };
      case BACKWARD:
        return new Function1D<DoubleMatrix1D, DoubleMatrix1D>() {

          @SuppressWarnings("synthetic-access")
          @Override
          public DoubleMatrix1D evaluate(final DoubleMatrix1D x) {
            Validate.notNull(x, "x");
            final double y = function.evaluate(x);
            final int n = x.getNumberOfElements();
            final double[] xData = x.getData();
            double oldValue;
            final double[] res = new double[n];
            for (int i = 0; i < n; i++) {
              oldValue = xData[i];
              xData[i] -= _eps;
              res[i] = (y - function.evaluate(x)) / _eps;
              xData[i] = oldValue;
            }
            return new DoubleMatrix1D(res);
          }
        };
      default:
        throw new IllegalArgumentException("Can only handle forward, backward and central differencing");
    }
  }

  public  ScalarFieldFirstOrderDifferentiator();
  public  ScalarFieldFirstOrderDifferentiator(final FiniteDifferenceType differenceType, final double eps);

}