/** Solves a standard form LP problem in the form of min(c) s.t. A.x = b lb <= x <= ub */
  protected int optimizeStandardLP(int nOfSlackVariables) throws Exception {
    log.info("optimizeStandardLP");

    LPOptimizationRequest lpRequest = getLPOptimizationRequest();
    if (log.isDebugEnabled() && lpRequest.isDumpProblem()) {
      log.debug("LP problem: " + lpRequest.toString());
    }

    LPOptimizationResponse lpResponse;
    if (lpRequest.isPresolvingDisabled()) {
      // optimization
      LPPrimalDualMethod opt = new LPPrimalDualMethod(minLBValue, maxUBValue);
      opt.setLPOptimizationRequest(lpRequest);
      if (opt.optimizePresolvedStandardLP() == OptimizationResponse.FAILED) {
        return OptimizationResponse.FAILED;
      }
      lpResponse = opt.getLPOptimizationResponse();
      setLPOptimizationResponse(lpResponse);
    } else {
      // presolving
      LPPresolver lpPresolver = new LPPresolver();
      lpPresolver.setAvoidScaling(lpRequest.isRescalingDisabled());
      lpPresolver.setAvoidFillIn(lpRequest.isAvoidPresolvingFillIn());
      lpPresolver.setAvoidIncreaseSparsity(lpRequest.isAvoidPresolvingIncreaseSparsity());
      testPresolver = lpPresolver; // just for testing
      lpPresolver.setNOfSlackVariables((short) nOfSlackVariables);
      lpPresolver.presolve(getC(), getA(), getB(), getLb(), getUb());
      int presolvedDim = lpPresolver.getPresolvedN();

      if (presolvedDim == 0) {
        // deterministic problem
        log.debug("presolvedDim : " + presolvedDim);
        log.debug("deterministic LP problem");
        lpResponse = new LPOptimizationResponse();
        lpResponse.setReturnCode(OptimizationResponse.SUCCESS);
        lpResponse.setSolution(new double[] {});
      } else {
        // solving the presolved problem
        DoubleMatrix1D presolvedC = lpPresolver.getPresolvedC();
        DoubleMatrix2D presolvedA = lpPresolver.getPresolvedA();
        DoubleMatrix1D presolvedB = lpPresolver.getPresolvedB();
        if (log.isDebugEnabled()) {
          if (lpPresolver.getPresolvedYlb() != null) {
            log.debug("Ylb: " + ArrayUtils.toString(lpPresolver.getPresolvedYlb().toArray()));
            log.debug("Yub: " + ArrayUtils.toString(lpPresolver.getPresolvedYub().toArray()));
          }
          if (lpPresolver.getPresolvedZlb() != null) {
            log.debug("Zlb: " + ArrayUtils.toString(lpPresolver.getPresolvedZlb().toArray()));
            log.debug("Zub: " + ArrayUtils.toString(lpPresolver.getPresolvedZub().toArray()));
          }
        }

        // new LP problem (the presolved problem)
        LPOptimizationRequest presolvedLPRequest = lpRequest.cloneMe();
        presolvedLPRequest.setC(presolvedC);
        presolvedLPRequest.setA(presolvedA);
        presolvedLPRequest.setB(presolvedB);
        presolvedLPRequest.setLb(lpPresolver.getPresolvedLB());
        presolvedLPRequest.setUb(lpPresolver.getPresolvedUB());
        presolvedLPRequest.setYlb(lpPresolver.getPresolvedYlb());
        presolvedLPRequest.setYub(lpPresolver.getPresolvedYub());
        presolvedLPRequest.setZlb(lpPresolver.getPresolvedZlb());
        presolvedLPRequest.setZub(lpPresolver.getPresolvedZub());
        if (getInitialPoint() != null) {
          presolvedLPRequest.setInitialPoint(lpPresolver.presolve(getInitialPoint().toArray()));
        }
        if (getNotFeasibleInitialPoint() != null) {
          presolvedLPRequest.setNotFeasibleInitialPoint(
              lpPresolver.presolve(getNotFeasibleInitialPoint().toArray()));
        }

        // optimization
        // NB: because of rescaling during the presolving phase, minLB and maxUB could have been
        // rescaled
        double rescaledMinLBValue =
            (Double.isNaN(lpPresolver.getMinRescaledLB()))
                ? this.minLBValue
                : lpPresolver.getMinRescaledLB();
        double rescaledMaxUBValue =
            (Double.isNaN(lpPresolver.getMaxRescaledUB()))
                ? this.maxUBValue
                : lpPresolver.getMaxRescaledUB();
        LPPrimalDualMethod opt = new LPPrimalDualMethod(rescaledMinLBValue, rescaledMaxUBValue);
        opt.setLPOptimizationRequest(presolvedLPRequest);
        if (opt.optimizePresolvedStandardLP() == OptimizationResponse.FAILED) {
          return OptimizationResponse.FAILED;
        }
        lpResponse = opt.getLPOptimizationResponse();
      }

      // postsolving
      double[] postsolvedSolution = lpPresolver.postsolve(lpResponse.getSolution());
      lpResponse.setSolution(postsolvedSolution);
      setLPOptimizationResponse(lpResponse);
    }

    return lpResponse.getReturnCode();
  }
  /** Solves an LP in the form of: min(c) s.t. A.x = b G.x < h lb <= x <= ub */
  @Override
  public int optimize() throws Exception {
    log.info("optimize");

    LPOptimizationRequest lpRequest = getLPOptimizationRequest();
    if (log.isDebugEnabled() && lpRequest.isDumpProblem()) {
      log.debug("LP problem: " + lpRequest.toString());
    }

    // standard form conversion
    LPStandardConverter lpConverter =
        new LPStandardConverter(); // the slack variables will have default unboundedUBValue

    lpConverter.toStandardForm(getC(), getG(), getH(), getA(), getB(), getLb(), getUb());
    int nOfSlackVariables = lpConverter.getStandardS();
    log.debug("nOfSlackVariables: " + nOfSlackVariables);
    DoubleMatrix1D standardC = lpConverter.getStandardC();
    DoubleMatrix2D standardA = lpConverter.getStandardA();
    DoubleMatrix1D standardB = lpConverter.getStandardB();
    DoubleMatrix1D standardLb = lpConverter.getStandardLB();
    DoubleMatrix1D standardUb = lpConverter.getStandardUB();

    // solve the standard form problem
    LPOptimizationRequest standardLPRequest = lpRequest.cloneMe();
    standardLPRequest.setC(standardC);
    standardLPRequest.setA(standardA);
    standardLPRequest.setB(standardB);
    standardLPRequest.setLb(
        ColtUtils.replaceValues(
            standardLb,
            lpConverter.getUnboundedLBValue(),
            minLBValue)); // substitute not-double numbers
    standardLPRequest.setUb(
        ColtUtils.replaceValues(
            standardUb,
            lpConverter.getUnboundedUBValue(),
            maxUBValue)); // substitute not-double numbers
    if (getInitialPoint() != null) {
      standardLPRequest.setInitialPoint(
          lpConverter.getStandardComponents(getInitialPoint().toArray()));
    }
    if (getNotFeasibleInitialPoint() != null) {
      standardLPRequest.setNotFeasibleInitialPoint(
          lpConverter.getStandardComponents(getNotFeasibleInitialPoint().toArray()));
    }

    // optimization
    LPPrimalDualMethod opt = new LPPrimalDualMethod(minLBValue, maxUBValue);
    opt.setLPOptimizationRequest(standardLPRequest);
    if (opt.optimizeStandardLP(nOfSlackVariables) == OptimizationResponse.FAILED) {
      return OptimizationResponse.FAILED;
    }

    // back to original form
    LPOptimizationResponse lpResponse = opt.getLPOptimizationResponse();
    double[] standardSolution = lpResponse.getSolution();
    double[] originalSol = lpConverter.postConvert(standardSolution);
    lpResponse.setSolution(originalSol);
    setLPOptimizationResponse(lpResponse);
    return lpResponse.getReturnCode();
  }