docs: private int getStep(double[] x, double[] dir, double[] newX, double f0,
        double g0, double[] newPt, double[] bestPt, double[] endPt,
        double stpMin, double stpMax) throws MaxEvaluationsExceeded {
      int info = 0;
  
      // Should check for input errors.
      boolean bound = false;
      double theta, gamma, p, q, r, s, stpc, stpq, stpf;
      double signG = newPt[g] * bestPt[g] / Math.abs(bestPt[g]);
  
      //
      // First case. A higher function value.
      // The minimum is bracketed. If the cubic step is closer
      // to stx than the quadratic step, the cubic step is taken,
      // else the average of the cubic and quadratic steps is taken.
      //
      if (newPt[f] > bestPt[f]) {
        info = 1;
        bound = true;
        theta = 3 * (bestPt[f] - newPt[f]) / (newPt[a] - bestPt[a]) + bestPt[g]
            + newPt[g];
        s = Math.max(Math.max(theta, newPt[g]), bestPt[g]);
        gamma = s
            * Math.sqrt((theta / s) * (theta / s) - (bestPt[g] / s)
                * (newPt[g] / s));
        if (newPt[a] < bestPt[a]) {
          gamma = -gamma;
        }
        p = (gamma - bestPt[g]) + theta;
        q = ((gamma - bestPt[g]) + gamma) + newPt[g];
        r = p / q;
        stpc = bestPt[a] + r * (newPt[a] - bestPt[a]);
        stpq = bestPt[a]
            + ((bestPt[g] / ((bestPt[f] - newPt[f]) / (newPt[a] - bestPt[a]) + bestPt[g])) / 2)
            * (newPt[a] - bestPt[a]);
  
        if (Math.abs(stpc - bestPt[a]) < Math.abs(stpq - bestPt[a])) {
          stpf = stpc;
        } else {
          stpf = stpq;
          // stpf = stpc + (stpq - stpc)/2;
        }
        bracketed = true;
        if (newPt[a] < 0.1) {
          stpf = 0.01 * stpf;
        }
  
      } else if (signG < 0.0) {
        //
        // Second case. A lower function value and derivatives of
        // opposite sign. The minimum is bracketed. If the cubic
        // step is closer to stx than the quadratic (secant) step,
        // the cubic step is taken, else the quadratic step is taken.
        //
        info = 2;
        bound = false;
        theta = 3 * (bestPt[f] - newPt[f]) / (newPt[a] - bestPt[a]) + bestPt[g]
            + newPt[g];
        s = Math.max(Math.max(theta, bestPt[g]), newPt[g]);
        gamma = s
            * Math.sqrt((theta / s) * (theta / s) - (bestPt[g] / s)
                * (newPt[g] / s));
        if (newPt[a] > bestPt[a]) {
          gamma = -gamma;
        }
        p = (gamma - newPt[g]) + theta;
        q = ((gamma - newPt[g]) + gamma) + bestPt[g];
        r = p / q;
        stpc = newPt[a] + r * (bestPt[a] - newPt[a]);
        stpq = newPt[a] + (newPt[g] / (newPt[g] - bestPt[g]))
            * (bestPt[a] - newPt[a]);
        if (Math.abs(stpc - newPt[a]) > Math.abs(stpq - newPt[a])) {
          stpf = stpc;
        } else {
          stpf = stpq;
        }
        bracketed = true;
  
      } else if (Math.abs(newPt[g]) < Math.abs(bestPt[g])) {
        //
        // Third case. A lower function value, derivatives of the
        // same sign, and the magnitude of the derivative decreases.
        // The cubic step is only used if the cubic tends to infinity
        // in the direction of the step or if the minimum of the cubic
        // is beyond stp. Otherwise the cubic step is defined to be
        // either stpmin or stpmax. The quadratic (secant) step is also
        // computed and if the minimum is bracketed then the the step
        // closest to stx is taken, else the step farthest away is taken.
        //
        info = 3;
        bound = true;
        theta = 3 * (bestPt[f] - newPt[f]) / (newPt[a] - bestPt[a]) + bestPt[g]
            + newPt[g];
        s = Math.max(Math.max(theta, bestPt[g]), newPt[g]);
        gamma = s
            * Math.sqrt(Math.max(0.0, (theta / s) * (theta / s) - (bestPt[g] / s)
                * (newPt[g] / s)));
        if (newPt[a] < bestPt[a]) {
          gamma = -gamma;
        }
        p = (gamma - bestPt[g]) + theta;
        q = ((gamma - bestPt[g]) + gamma) + newPt[g];
        r = p / q;
        if (r < 0.0 && gamma != 0.0) {
          stpc = newPt[a] + r * (bestPt[a] - newPt[a]);
        } else if (newPt[a] > bestPt[a]) {
          stpc = stpMax;
        } else {
          stpc = stpMin;
        }
        stpq = newPt[a] + (newPt[g] / (newPt[g] - bestPt[g]))
            * (bestPt[a] - newPt[a]);
  
        if (bracketed) {
          if (Math.abs(newPt[a] - stpc) < Math.abs(newPt[a] - stpq)) {
            stpf = stpc;
          } else {
            stpf = stpq;
          }
        } else {
          if (Math.abs(newPt[a] - stpc) > Math.abs(newPt[a] - stpq)) {
            stpf = stpc;
          } else {
            stpf = stpq;
          }
        }
  
      } else {
        //
        // Fourth case. A lower function value, derivatives of the
        // same sign, and the magnitude of the derivative does
        // not decrease. If the minimum is not bracketed, the step
        // is either stpmin or stpmax, else the cubic step is taken.
        //
        info = 4;
        bound = false;
  
        if (bracketed) {
          theta = 3 * (bestPt[f] - newPt[f]) / (newPt[a] - bestPt[a]) + bestPt[g]
              + newPt[g];
          s = Math.max(Math.max(theta, bestPt[g]), newPt[g]);
          gamma = s
              * Math.sqrt((theta / s) * (theta / s) - (bestPt[g] / s)
                  * (newPt[g] / s));
          if (newPt[a] > bestPt[a]) {
            gamma = -gamma;
          }
          p = (gamma - newPt[g]) + theta;
          q = ((gamma - newPt[g]) + gamma) + bestPt[g];
          r = p / q;
          stpc = newPt[a] + r * (bestPt[a] - newPt[a]);
          stpf = stpc;
        } else if (newPt[a] > bestPt[a]) {
          stpf = stpMax;
        } else {
          stpf = stpMin;
        }
  
      }
  
      //
      // Update the interval of uncertainty. This update does not
      // depend on the new step or the case analysis above.
      //
      if (newPt[f] > bestPt[f]) {
        copy(newPt, endPt);
      } else {
        if (signG < 0.0) {
          copy(bestPt, endPt);
        }
        copy(newPt, bestPt);
      }
  
      say(String.valueOf(info));
  
      //
      // Compute the new step and safeguard it.
      //
      stpf = Math.min(stpMax, stpf);
      stpf = Math.max(stpMin, stpf);
      newPt[a] = stpf;
  
      if (bracketed && bound) {
        if (endPt[a] > bestPt[a]) {
          newPt[a] = Math.min(bestPt[a] + p66 * (endPt[a] - bestPt[a]), newPt[a]);
        } else {
          newPt[a] = Math.max(bestPt[a] + p66 * (endPt[a] - bestPt[a]), newPt[a]);
        }
      }
  
      return info;
    }