/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                       */
/*    This file is part of the HiGHS linear optimization suite           */
/*                                                                       */
/*    Written and engineered 2008-2019 at the University of Edinburgh    */
/*                                                                       */
/*    Available as open-source under the MIT License                     */
/*                                                                       */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**@file simplex/SpeciaLps.h
 * @brief Utilities for tests with special LPs
 */
#ifndef SIMPLEX_SPECIALPS_H_
#define SIMPLEX_SPECIALPS_H_

#include <vector>

#include "lp_data/HConst.h"
#include "lp_data/HighsLp.h"

const double inf = kHighsInf;

class SpecialLps {
 public:
  void issue272Lp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "issue272";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {3, 2};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {23, 10};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {3, 5, 6, 2};
    lp.sense_ = ObjSense::kMaximize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = 8.83333333333333;
  }

  void issue280Lp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "issue280";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {-1, 1};
    lp.col_lower_ = {1, 2};
    lp.col_upper_ = {1, 2};
    lp.row_lower_ = {-inf, 2};
    lp.row_upper_ = {1, 2};
    lp.a_matrix_.start_ = {0, 1, 2};
    lp.a_matrix_.index_ = {0, 1};
    lp.a_matrix_.value_ = {1, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = 1;
  }

  void issue282Lp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "issue282";
    lp.num_col_ = 2;
    lp.num_row_ = 3;
    lp.col_cost_ = {-3, -2};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf, -inf};
    lp.row_upper_ = {10, 8, 4};
    lp.a_matrix_.start_ = {0, 3, 5};
    lp.a_matrix_.index_ = {0, 1, 2, 0, 1};
    lp.a_matrix_.value_ = {2, 1, 1, 1, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = -18;
  }

  void issue285Lp(HighsLp& lp, HighsModelStatus& require_model_status) {
    lp.model_name_ = "issue285";
    lp.num_col_ = 2;
    lp.num_row_ = 3;
    lp.col_cost_ = {-4, 1};
    lp.col_lower_ = {2, 0};
    lp.col_upper_ = {2, inf};
    lp.row_lower_ = {-inf, -inf, -inf};
    lp.row_upper_ = {14, 0, 3};
    lp.a_matrix_.start_ = {0, 2, 5};
    lp.a_matrix_.index_ = {0, 2, 0, 1, 2};
    lp.a_matrix_.value_ = {7, 2, -2, 1, -2};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void issue295Lp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "issue295";
    lp.num_col_ = 5;
    lp.num_row_ = 2;
    lp.col_cost_ = {0, 0, 0, 1, -1};
    lp.col_lower_ = {-inf, -inf, -inf, -1, -1};
    lp.col_upper_ = {inf, inf, inf, 1, 1};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {2, -2};
    lp.a_matrix_.start_ = {0, 1, 2, 2, 2, 2};
    lp.a_matrix_.index_ = {0, 1};
    lp.a_matrix_.value_ = {1, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = -2;
  }

  void issue306Lp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "issue30";
    lp.num_col_ = 10;
    lp.num_row_ = 6;
    lp.col_cost_ = {-1.64, 0.7,  1.8,  -1.06, -1.16,
                    0.26,  2.13, 1.53, 0.66,  0.28};
    lp.col_lower_ = {-0.84, -0.97, 0.34, 0.4,   -0.33,
                     -0.74, 0.47,  0.09, -1.45, -0.73};
    lp.col_upper_ = {0.37, 0.02, 2.86, 0.86, 1.18,
                     0.5,  1.76, 0.17, 0.32, -0.15};
    lp.row_lower_ = {0.9626, -1e+200, -1e+200, -1e+200, -1e+200, -1e+200};
    lp.row_upper_ = {0.9626, 0.615, 0, 0.172, -0.869, -0.022};
    lp.a_matrix_.start_ = {0, 0, 1, 2, 5, 5, 6, 7, 9, 10, 12};
    lp.a_matrix_.index_ = {4, 4, 0, 1, 3, 0, 4, 1, 5, 0, 1, 4};
    lp.a_matrix_.value_ = {-1.22, -0.25, 0.93,  1.18, 0.43,  0.65,
                           -2.06, -0.2,  -0.25, 0.83, -0.22, 1.37};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = -1.191;
  }

  void issue425Lp(HighsLp& lp, HighsModelStatus& require_model_status) {
    lp.model_name_ = "issue425";
    lp.num_col_ = 4;
    lp.num_row_ = 4;
    lp.col_cost_ = {1, 1, 1, 2};
    lp.col_lower_ = {0, 0, 0, 0};
    lp.col_upper_ = {inf, inf, inf, inf};
    lp.row_lower_ = {1, 2, 2, 4};
    lp.row_upper_ = {1, 2, 2, 4};
    lp.a_matrix_.start_ = {0, 3, 5, 6, 7};
    lp.a_matrix_.index_ = {0, 2, 3, 1, 3, 3, 3};
    lp.a_matrix_.value_ = {1, 1, 1, 2, 1, 1, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void issue669Lp(HighsLp& lp, HighsModelStatus& require_model_status) {
    lp.model_name_ = "issue669";
    lp.num_col_ = 27;
    lp.num_row_ = 9;
    vector<double> zero_vector_col;
    vector<double> zero_vector_row;
    vector<HighsInt> zero_vector_start;
    for (int iCol = 0; iCol < lp.num_col_; iCol++) zero_vector_col.push_back(0);
    for (int iRow = 0; iRow < lp.num_row_; iRow++) zero_vector_row.push_back(0);
    lp.col_cost_ = zero_vector_col;
    lp.col_lower_ = zero_vector_col;
    lp.col_upper_ = zero_vector_col;
    lp.row_lower_ = zero_vector_row;
    lp.row_upper_ = zero_vector_row;
    for (int iCol = 0; iCol < lp.num_col_; iCol++)
      lp.a_matrix_.start_.push_back(0);
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
  }

  void primalDualInfeasible1Lp(HighsLp& lp,
                               HighsModelStatus& require_model_status) {
    lp.model_name_ = "primalDualInfeasible1";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {2, -1};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {1, -2};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {1, -1, -1, 1};
    lp.sense_ = ObjSense::kMaximize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void primalDualInfeasible2Lp(HighsLp& lp,
                               HighsModelStatus& require_model_status) {
    lp.model_name_ = "primalDualInfeasible2";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {1, 1};
    lp.col_lower_ = {-inf, -inf};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {0, -1};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {1, -1, -1, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void primalDualInfeasible3Lp(HighsLp& lp,
                               HighsModelStatus& require_model_status) {
    lp.model_name_ = "primalDualInfeasible3";
    lp.num_col_ = 3;
    lp.num_row_ = 2;
    lp.col_cost_ = {-1, 0, 0};
    lp.col_lower_ = {0, 0, 0};
    lp.col_upper_ = {inf, inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {1, -2};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {1, 2, 1, 2};
    lp.a_matrix_.value_ = {1, 1, 1, -1};
    lp.a_matrix_.format_ = MatrixFormat::kRowwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void scipLpi2Lp(HighsLp& lp, HighsModelStatus& require_model_status) {
    lp.model_name_ = "scipLpi2";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {3, 1};
    lp.col_lower_ = {-inf, -inf};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {10, 15};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {2, 1, 1, 3};
    lp.sense_ = ObjSense::kMaximize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kUnbounded;
  }

  void scipLpi3Lp(HighsLp& lp, HighsModelStatus& require_model_status) {
    lp.model_name_ = "scipLpi3";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {10, 15};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {3, 1};
    lp.row_upper_ = {3, 1};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {2, 1, 1, 3};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kInfeasible;
  }

  void distillationLp(HighsLp& lp, HighsModelStatus& require_model_status,
                      double& optimal_objective) {
    lp.model_name_ = "distillation";
    lp.num_col_ = 2;
    lp.num_row_ = 3;
    lp.col_cost_ = {8, 10};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {7, 12, 6};
    lp.row_upper_ = {inf, inf, inf};
    lp.a_matrix_.start_ = {0, 3, 6};
    lp.a_matrix_.index_ = {0, 1, 2, 0, 1, 2};
    lp.a_matrix_.value_ = {2, 3, 2, 2, 4, 1};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = 31.2;
  }

  void distillationMip(HighsLp& lp, HighsModelStatus& require_model_status,
                       double& optimal_objective) {
    distillationLp(lp, require_model_status, optimal_objective);
    lp.integrality_ = {HighsVarType::kInteger, HighsVarType::kInteger};
    optimal_objective = 32.0;
  }

  void blendingLp(HighsLp& lp, HighsModelStatus& require_model_status,
                  double& optimal_objective) {
    lp.model_name_ = "blending";
    lp.num_col_ = 2;
    lp.num_row_ = 2;
    lp.col_cost_ = {-8, -10};
    lp.col_lower_ = {0, 0};
    lp.col_upper_ = {inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {120, 210};
    lp.a_matrix_.start_ = {0, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {0.3, 0.7, 0.5, 0.5};
    lp.sense_ = ObjSense::kMinimize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = -2850;
  }

  void blendingMaxLp(HighsLp& lp, HighsModelStatus& require_model_status,
                     double& optimal_objective) {
    blendingLp(lp, require_model_status, optimal_objective);
    lp.model_name_ = "blendingMax";
    for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
      lp.col_cost_[iCol] = -lp.col_cost_[iCol];
    lp.sense_ = ObjSense::kMaximize;
    optimal_objective = -optimal_objective;
  }

  void ThreeDLp(HighsLp& lp, HighsModelStatus& require_model_status,
                double& optimal_objective) {
    lp.model_name_ = "3-d LP";
    lp.num_col_ = 3;
    lp.num_row_ = 2;
    lp.col_cost_ = {1, 2, 3};
    lp.col_lower_ = {0, 0, 0};
    lp.col_upper_ = {inf, inf, inf};
    lp.row_lower_ = {-inf, -inf};
    lp.row_upper_ = {3, 2};
    lp.a_matrix_.start_ = {0, 1, 2, 4};
    lp.a_matrix_.index_ = {0, 1, 0, 1};
    lp.a_matrix_.value_ = {1, 1, 2, 2};
    lp.sense_ = ObjSense::kMaximize;
    lp.offset_ = 0;
    lp.a_matrix_.format_ = MatrixFormat::kColwise;
    require_model_status = HighsModelStatus::kOptimal;
    optimal_objective = 7;
  }

  void reportIssue(const HighsInt issue, const bool dev_run = false) {
    if (dev_run)
      printf("\n *************\n * Issue %3" HIGHSINT_FORMAT
             " *\n *************\n",
             issue);
  }

  void reportLpName(const std::string lp_name, const bool dev_run = false) {
    if (dev_run) {
      HighsInt lp_name_length = lp_name.length();
      printf("\n **");
      for (HighsInt i = 0; i < lp_name_length; i++) printf("*");
      printf("**\n * %s *\n **", lp_name.c_str());
      for (HighsInt i = 0; i < lp_name_length; i++) printf("*");
      printf("**\n");
    }
  }

  bool objectiveOk(const double optimal_objective,
                   const double require_optimal_objective,
                   const bool dev_run = false) {
    double error = std::fabs(optimal_objective - require_optimal_objective) /
                   std::max(1.0, std::fabs(require_optimal_objective));
    bool error_ok = error < 1e-10;
    if (!error_ok && dev_run)
      printf("Objective is %g but require %g (error %g)\n", optimal_objective,
             require_optimal_objective, error);
    return error_ok;
  }

  void reportSolution(Highs& highs, const bool dev_run = false) {
    if (!dev_run) return;
    const HighsInfo& info = highs.getInfo();
    if (info.primal_solution_status == kSolutionStatusFeasible) {
      const HighsSolution& solution = highs.getSolution();
      printf("Solution\n");
      printf("Col       Value        Dual\n");
      for (HighsInt iCol = 0; iCol < highs.getLp().num_col_; iCol++)
        printf("%3" HIGHSINT_FORMAT " %11.4g %11.4g\n", iCol,
               solution.col_value[iCol], solution.col_dual[iCol]);
      printf("Row       Value        Dual\n");
      for (HighsInt iRow = 0; iRow < highs.getLp().num_row_; iRow++)
        printf("%3" HIGHSINT_FORMAT " %11.4g %11.4g\n", iRow,
               solution.row_value[iRow], solution.row_dual[iRow]);
    } else {
      printf("info.primal_solution_status = %" HIGHSINT_FORMAT "\n",
             info.primal_solution_status);
    }
  }
};
#endif /* SIMPLEX_SPECIALPS_H_ */