class RungeKuttaIntegrator1D extends Integrator1D<Double, Double> {

  @Override
  public Double integrate(final Function1D<Double, Double> f, final Double lower, final Double upper) {
    ArgumentChecker.notNull(lower, "lower");
    ArgumentChecker.notNull(upper, "upper");
    if (Double.isNaN(lower) || Double.isInfinite(lower) || Double.isInfinite(upper) || Double.isNaN(upper)) {
      throw new OpenGammaRuntimeException("lower or upper was NaN or Inf");
    }
    
    final double h = (upper - lower) / _minSteps;
    double f1, f2, f3, x;
    x = lower;
    f1 = f.evaluate(x);
    if (Double.isNaN(f1) || Double.isInfinite(f1)) {
      throw new OpenGammaRuntimeException("function evaluation returned NaN or Inf");
    }

    double result = 0.0;
    for (int i = 0; i < _minSteps; i++) {
      f2 = f.evaluate(x + h / 2.0);
      if (Double.isNaN(f2) || Double.isInfinite(f2)) {
        throw new OpenGammaRuntimeException("function evaluation returned NaN or Inf");
      }
      f3 = f.evaluate(x + h);
      if (Double.isNaN(f3) || Double.isInfinite(f3)) {
        throw new OpenGammaRuntimeException("function evaluation returned NaN or Inf");
      }

      result += calculateRungeKuttaFourthOrder(f, x, h, f1, f2, f3);
      f1 = f3;
      x += h;
    }
    return result;
  }

  public  RungeKuttaIntegrator1D(final double absTol, final double relTol, final int minSteps);
  public  RungeKuttaIntegrator1D(final double tol, final int minSteps);
  public  RungeKuttaIntegrator1D(final double atol, final double rtol);
  public  RungeKuttaIntegrator1D(final double tol);
  public  RungeKuttaIntegrator1D(final int minSteps);
  public  RungeKuttaIntegrator1D();

}