private <T extends RealFieldElement<T>> RungeKuttaStepInterpolator convertInterpolator(
      final RungeKuttaFieldStepInterpolator<T> fieldInterpolator,
      final FirstOrderFieldDifferentialEquations<T> eqn) {

    RungeKuttaStepInterpolator regularInterpolator = null;
    try {

      String interpolatorName = fieldInterpolator.getClass().getName();
      String integratorName = interpolatorName.replaceAll("Field", "");
      @SuppressWarnings("unchecked")
      Class<RungeKuttaStepInterpolator> clz =
          (Class<RungeKuttaStepInterpolator>) Class.forName(integratorName);
      regularInterpolator = clz.newInstance();

      double[][] yDotArray = null;
      java.lang.reflect.Field fYD = RungeKuttaFieldStepInterpolator.class.getDeclaredField("yDotK");
      fYD.setAccessible(true);
      @SuppressWarnings("unchecked")
      T[][] fieldYDotk = (T[][]) fYD.get(fieldInterpolator);
      yDotArray = new double[fieldYDotk.length][];
      for (int i = 0; i < yDotArray.length; ++i) {
        yDotArray[i] = new double[fieldYDotk[i].length];
        for (int j = 0; j < yDotArray[i].length; ++j) {
          yDotArray[i][j] = fieldYDotk[i][j].getReal();
        }
      }
      double[] y = new double[yDotArray[0].length];

      EquationsMapper primaryMapper = null;
      EquationsMapper[] secondaryMappers = null;
      java.lang.reflect.Field fMapper =
          AbstractFieldStepInterpolator.class.getDeclaredField("mapper");
      fMapper.setAccessible(true);
      @SuppressWarnings("unchecked")
      FieldEquationsMapper<T> mapper = (FieldEquationsMapper<T>) fMapper.get(fieldInterpolator);
      java.lang.reflect.Field fStart = FieldEquationsMapper.class.getDeclaredField("start");
      fStart.setAccessible(true);
      int[] start = (int[]) fStart.get(mapper);
      primaryMapper = new EquationsMapper(start[0], start[1]);
      secondaryMappers = new EquationsMapper[mapper.getNumberOfEquations() - 1];
      for (int i = 0; i < secondaryMappers.length; ++i) {
        secondaryMappers[i] = new EquationsMapper(start[i + 1], start[i + 2]);
      }

      AbstractIntegrator dummyIntegrator =
          new AbstractIntegrator("dummy") {
            @Override
            public void integrate(ExpandableStatefulODE equations, double t) {
              Assert.fail("this method should not be called");
            }

            @Override
            public void computeDerivatives(final double t, final double[] y, final double[] yDot) {
              T fieldT = fieldInterpolator.getCurrentState().getTime().getField().getZero().add(t);
              T[] fieldY =
                  MathArrays.buildArray(
                      fieldInterpolator.getCurrentState().getTime().getField(), y.length);
              for (int i = 0; i < y.length; ++i) {
                fieldY[i] =
                    fieldInterpolator.getCurrentState().getTime().getField().getZero().add(y[i]);
              }
              T[] fieldYDot = eqn.computeDerivatives(fieldT, fieldY);
              for (int i = 0; i < yDot.length; ++i) {
                yDot[i] = fieldYDot[i].getReal();
              }
            }
          };
      regularInterpolator.reinitialize(
          dummyIntegrator,
          y,
          yDotArray,
          fieldInterpolator.isForward(),
          primaryMapper,
          secondaryMappers);

      T[] fieldPreviousY = fieldInterpolator.getPreviousState().getState();
      for (int i = 0; i < y.length; ++i) {
        y[i] = fieldPreviousY[i].getReal();
      }
      regularInterpolator.storeTime(fieldInterpolator.getPreviousState().getTime().getReal());

      regularInterpolator.shift();

      T[] fieldCurrentY = fieldInterpolator.getCurrentState().getState();
      for (int i = 0; i < y.length; ++i) {
        y[i] = fieldCurrentY[i].getReal();
      }
      regularInterpolator.storeTime(fieldInterpolator.getCurrentState().getTime().getReal());

    } catch (ClassNotFoundException cnfe) {
      Assert.fail(cnfe.getLocalizedMessage());
    } catch (InstantiationException ie) {
      Assert.fail(ie.getLocalizedMessage());
    } catch (IllegalAccessException iae) {
      Assert.fail(iae.getLocalizedMessage());
    } catch (NoSuchFieldException nsfe) {
      Assert.fail(nsfe.getLocalizedMessage());
    } catch (IllegalArgumentException iae) {
      Assert.fail(iae.getLocalizedMessage());
    }

    return regularInterpolator;
  }
  /** {@inheritDoc} */
  @Override
  public void integrate(final ExpandableStatefulODE equations, final double t)
      throws MathIllegalStateException, MathIllegalArgumentException {

    sanityChecks(equations, t);
    setEquations(equations);
    final boolean forward = t > equations.getTime();

    // create some internal working arrays
    final double[] y0 = equations.getCompleteState();
    final double[] y = y0.clone();
    final int stages = c.length + 1;
    final double[][] yDotK = new double[stages][y.length];
    final double[] yTmp = y0.clone();
    final double[] yDotTmp = new double[y.length];

    // set up an interpolator sharing the integrator arrays
    final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
    interpolator.reinitialize(
        this, yTmp, yDotK, forward, equations.getPrimaryMapper(), equations.getSecondaryMappers());
    interpolator.storeTime(equations.getTime());

    // set up integration control objects
    stepStart = equations.getTime();
    double hNew = 0;
    boolean firstTime = true;
    initIntegration(equations.getTime(), y0, t);

    // main integration loop
    isLastStep = false;
    do {

      interpolator.shift();

      // iterate over step size, ensuring local normalized error is smaller than 1
      double error = 10;
      while (error >= 1.0) {

        if (firstTime || !fsal) {
          // first stage
          computeDerivatives(stepStart, y, yDotK[0]);
        }

        if (firstTime) {
          final double[] scale = new double[mainSetDimension];
          if (vecAbsoluteTolerance == null) {
            for (int i = 0; i < scale.length; ++i) {
              scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * FastMath.abs(y[i]);
            }
          } else {
            for (int i = 0; i < scale.length; ++i) {
              scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * FastMath.abs(y[i]);
            }
          }
          hNew = initializeStep(forward, getOrder(), scale, stepStart, y, yDotK[0], yTmp, yDotK[1]);
          firstTime = false;
        }

        stepSize = hNew;
        if (forward) {
          if (stepStart + stepSize >= t) {
            stepSize = t - stepStart;
          }
        } else {
          if (stepStart + stepSize <= t) {
            stepSize = t - stepStart;
          }
        }

        // next stages
        for (int k = 1; k < stages; ++k) {

          for (int j = 0; j < y0.length; ++j) {
            double sum = a[k - 1][0] * yDotK[0][j];
            for (int l = 1; l < k; ++l) {
              sum += a[k - 1][l] * yDotK[l][j];
            }
            yTmp[j] = y[j] + stepSize * sum;
          }

          computeDerivatives(stepStart + c[k - 1] * stepSize, yTmp, yDotK[k]);
        }

        // estimate the state at the end of the step
        for (int j = 0; j < y0.length; ++j) {
          double sum = b[0] * yDotK[0][j];
          for (int l = 1; l < stages; ++l) {
            sum += b[l] * yDotK[l][j];
          }
          yTmp[j] = y[j] + stepSize * sum;
        }

        // estimate the error at the end of the step
        error = estimateError(yDotK, y, yTmp, stepSize);
        if (error >= 1.0) {
          // reject the step and attempt to reduce error by stepsize control
          final double factor =
              FastMath.min(
                  maxGrowth, FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
          hNew = filterStep(stepSize * factor, forward, false);
        }
      }

      // local error is small enough: accept the step, trigger events and step handlers
      interpolator.storeTime(stepStart + stepSize);
      System.arraycopy(yTmp, 0, y, 0, y0.length);
      System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
      stepStart = acceptStep(interpolator, y, yDotTmp, t);
      System.arraycopy(y, 0, yTmp, 0, y.length);

      if (!isLastStep) {

        // prepare next step
        interpolator.storeTime(stepStart);

        if (fsal) {
          // save the last evaluation for the next step
          System.arraycopy(yDotTmp, 0, yDotK[0], 0, y0.length);
        }

        // stepsize control for next step
        final double factor =
            FastMath.min(maxGrowth, FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
        final double scaledH = stepSize * factor;
        final double nextT = stepStart + scaledH;
        final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
        hNew = filterStep(scaledH, forward, nextIsLast);

        final double filteredNextT = stepStart + hNew;
        final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
        if (filteredNextIsLast) {
          hNew = t - stepStart;
        }
      }

    } while (!isLastStep);

    // dispatch results
    equations.setTime(stepStart);
    equations.setCompleteState(y);

    resetInternalState();
  }