/**
   * Create a multi-start optimizer from a single-start optimizer.
   *
   * @param optimizer Single-start optimizer to wrap.
   * @param starts Number of starts to perform. If {@code starts == 1}, the {@code optimize} methods
   *     will return the same solution as {@code optimizer} would.
   * @param generator Random generator to use for restarts.
   * @throws NotStrictlyPositiveException if {@code starts < 1}.
   */
  public MultiStartUnivariateOptimizer(
      final UnivariateOptimizer optimizer, final int starts, final RandomGenerator generator) {
    super(optimizer.getConvergenceChecker());

    if (starts < 1) {
      throw new NotStrictlyPositiveException(starts);
    }

    this.optimizer = optimizer;
    this.starts = starts;
    this.generator = generator;
  }
  /** {@inheritDoc} */
  @Override
  protected UnivariatePointValuePair doOptimize() {
    // Remove all instances of "MaxEval" and "SearchInterval" from the
    // array that will be passed to the internal optimizer.
    // The former is to enforce smaller numbers of allowed evaluations
    // (according to how many have been used up already), and the latter
    // to impose a different start value for each start.
    for (int i = 0; i < optimData.length; i++) {
      if (optimData[i] instanceof MaxEval) {
        optimData[i] = null;
        maxEvalIndex = i;
        continue;
      }
      if (optimData[i] instanceof SearchInterval) {
        optimData[i] = null;
        searchIntervalIndex = i;
        continue;
      }
    }
    if (maxEvalIndex == -1) {
      throw new MathIllegalStateException();
    }
    if (searchIntervalIndex == -1) {
      throw new MathIllegalStateException();
    }

    RuntimeException lastException = null;
    optima = new UnivariatePointValuePair[starts];
    totalEvaluations = 0;

    final int maxEval = getMaxEvaluations();
    final double min = getMin();
    final double max = getMax();
    final double startValue = getStartValue();

    // Multi-start loop.
    for (int i = 0; i < starts; i++) {
      // CHECKSTYLE: stop IllegalCatch
      try {
        // Decrease number of allowed evaluations.
        optimData[maxEvalIndex] = new MaxEval(maxEval - totalEvaluations);
        // New start value.
        final double s = (i == 0) ? startValue : min + generator.nextDouble() * (max - min);
        optimData[searchIntervalIndex] = new SearchInterval(min, max, s);
        // Optimize.
        optima[i] = optimizer.optimize(optimData);
      } catch (RuntimeException mue) {
        lastException = mue;
        optima[i] = null;
      }
      // CHECKSTYLE: resume IllegalCatch

      totalEvaluations += optimizer.getEvaluations();
    }

    sortPairs(getGoalType());

    if (optima[0] == null) {
      throw lastException; // Cannot be null if starts >= 1.
    }

    // Return the point with the best objective function value.
    return optima[0];
  }