/* glpsol.c */

/*----------------------------------------------------------------------
-- Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@mai2.rcnet.ru>,
--               Department for Applied Informatics, Moscow Aviation
--               Institute, Moscow, Russia. All rights reserved.
--
-- This file is a part of GLPK (GNU Linear Programming Kit).
--
-- GLPK is free software; you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2, or (at your option)
-- any later version.
--
-- GLPK is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-- License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with GLPK; see the file COPYING. If not, write to the Free
-- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-- 02111-1307, USA.
----------------------------------------------------------------------*/

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "glpk.h"
#include "glplib.h"

#define error print

/*----------------------------------------------------------------------
-- This program is a stand-alone LP/MIP solver. For pure LP problems
-- either the revised simplex method or the primal dual interior-point
-- method can be used. For MIP problems the branch-and-bound procedure
-- based on the revised simplex method is used.
----------------------------------------------------------------------*/

static char *version = "GLPSOL -- GLPK LP/MIP Solver, Version 3.0.7";

static char *in_fname = NULL;
/* name of the input text file */

static int format = 'M';
/* format of the input text file:
   'M' - MPS
   'L' - GLPK/L */

static int check = 0;
/* if this flag is set, only input data checking is required */

static char *fn_gener = NULL;
/* name of the output text file, to which the routine glp_read_lpm()
   should output generated LP/MIP problem in plain text format; NULL
   means no output */

static int obj_dir = 0;
/* optimization direction flag:
    0  - not specified
   '-' - minimization
   '+' - maximization */

static int option = 2;
/* this parameter tells the solver what solution should be found:
   0 - initial solution
   1 - primal feasible solution
   2 - optimal solution */

static int method = 'S';
/* the method which should be used for solving a problem:
   'O' - simplex method, old version (based on rsm1_driver)
   'S' - simplex method, new version (based on glp_simplex2)
   'I' - interior point method */

static char *out_fname = NULL;
/* name of the output text file to which the final solution should be
   sent using printable format (optional) */

static int scale = 1;
/* if this flag is set, the solver performs automatic scaling the
   problem before solving; otherwise scaling is not used */

static int use_dual = 0;
/* if this flag is set, the solver uses the dual simplex method for
   searching for feasible solution; otherwise the solver uses the primal
   simplex method */

static int steep = 1;
/* if this flag is set, the solver uses the steepest edge pricing (for
   both primal and dual simplex) proposed by Goldfarb & Reid; otherwise
   the solver uses the standard "textbook" pricing */

static int relax = 1;
/* if this flag is set, the solver uses two-pass ratio test (for both
   primal and dual simplex) proposed by P.Harris; otherwise the solver
   uses the standard "textbook" ratio test */

static int nomip = 0;
/* if this flag is set, the solver considers all integer variables as
   continuous (this allows solving MIP problem as pure LP) */

static int branch = BB_DRTOM;
/* this parameter specifies what branching technique should be used by
   the solver:
   BB_FIRST - branch on the first integer variable
   BB_LAST  - branch on the last integer variable
   BB_DRTOM - branch using heuristic by Driebeek and Tomlin */

static int btrack = BB_BESTP;
/* this parameter specifies what backtracking technique should be used
   by the solver:
   BB_FIFO  - backtrack using FIFO heuristic
   BB_LIFO  - backtrack using LIFO heuristic
   BB_BESTP - backtrack using the best projection heuristic */

/*----------------------------------------------------------------------
-- display_help - display help.
--
-- This routine displays help information about the program as it is
-- required by the GNU Coding Standards. */

static void display_help(char *my_name)
{     print("Usage: %s [options...] filename", my_name);
      print("");
      print("General options:");
      print("   --mps             read LP/MIP data using MPS format (de"
         "fault)");
      print("   --lpm             read LP/MIP model written in GLPK/L m"
         "odeling");
      print("                     language");
      print("   --check           do not solve the problem, check input"
         " data only");
      print("   --gener filename  send generated LP/MIP problem to file"
         "name using");
      print("                     plain text format (has meaning for --"
         "lpm only)");
      print("   --min             minimize the objective function");
      print("   --max             maximize the objective function");
      print("   --ini             find initial solution only");
      print("   --any             find any feasible solution");
      print("   --fin             find final solution (default)");
      print("   --old-sim         use simplex method, old version");
      print("   --simplex         use simplex method, new version (defa"
         "ult)");
      print("   --interior        use interior point method (for pure L"
         "P only)");
      print("   -o filename, --output filename");
      print("                     send solution to filename using print"
         "able format");
      print("   --scale           scale the problem (default)");
      print("   --noscale         do not scale the problem");
      print("   -h, --help        display this help information and exi"
         "t");
      print("   -v, --version     display program version and exit");
      print("");
      print("Options specific to simplex method (--old-sim assumed):");
      print("   --primal          find feasible solution using primal s"
         "implex");
      print("                     (default)");
      print("   --dual            find feasible solution using dual sim"
         "plex");
      print("   --steep           use steepest edge technique (default)"
         );
      print("   --nosteep         use standard \"textbook\" pricing");
      print("   --relax           use Harris' two-pass ratio test (defa"
         "ult)");
      print("   --norelax         use standard \"textbook\" ratio test")
         ;
      print("");
      print("Options specific to MIP:");
      print("   --nomip           consider all integer variables as con"
         "tinuous");
      print("                     (this allows solving MIP problem as p"
         "ure LP)");
      print("   --first           branch on first integer variable");
      print("   --last            branch on last integer variable");
      print("   --drtom           branch using heuristic by Driebeck an"
         "d Tomlin");
      print("                     (default)");
      print("   --fifo            backtrack using FIFO heuristic");
      print("   --lifo            backtrack using LIFO heuristic");
      print("   --bestp           backtrack using the best projection h"
         "euristic");
      print("                     (default)");
      print("");
      print("See the official GNU webpage dedicated to GLPK at");
      print("<http://www.gnu.org/software/glpk/glpk.html>");
      print("");
      print("Please, report bugs to <bug-glpk@gnu.org>");
      exit(EXIT_SUCCESS);
      /* no return */
}

/*----------------------------------------------------------------------
-- display_version - display version.
--
-- This routine displays version information for the program as it is
-- required by the GNU Coding Standards. */

static void display_version(void)
{     print("%s", version);
      print("Copyright (C) 2000, 2001, 2002 Andrew Makhorin <mao@gnu.or"
         "g>");
      print("This program is free software; you may redistribute it und"
         "er the terms of");
      print("the GNU General Public License. This program has absolutel"
         "y no warranty.");
      exit(EXIT_SUCCESS);
      /* no return */
}

/*----------------------------------------------------------------------
-- process_cmdline - process command line parameters.
--
-- This routine processes parameters specified in command line. */

#define p(str) (strcmp(argv[k], str) == 0)

static void process_cmdline(int argc, char *argv[])
{     int k;
      for (k = 1; k < argc; k++)
      {  if (p("--mps"))
            format = 'M';
         else if (p("--lpm"))
            format = 'L';
         else if (p("--check"))
            check = 1;
         else if (p("--gener"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  error("No gener file name specified");
               exit(EXIT_FAILURE);
            }
            fn_gener = argv[k];
         }
         else if (p("--min"))
            obj_dir = '-';
         else if (p("--max"))
            obj_dir = '+';
         else if (p("--ini"))
            option = 0;
         else if (p("--any"))
            option = 1;
         else if (p("--fin"))
            option = 2;
         else if (p("--old-sim"))
            method = 'O';
         else if (p("--simplex"))
            method = 'S';
         else if (p("--interior"))
            method = 'I';
         else if (p("-o") || p("--output"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  error("No output file name specified");
               exit(EXIT_FAILURE);
            }
            out_fname = argv[k];
         }
         else if (p("--scale"))
            scale = 1;
         else if (p("--noscale"))
            scale = 0;
         else if (p("-h") || p("--help"))
            display_help(argv[0]);
         else if (p("-v") || p("--version"))
            display_version();
         else if (p("--primal"))
            use_dual = 0;
         else if (p("--dual"))
            use_dual = 1;
         else if (p("--steep"))
            steep = 1;
         else if (p("--nosteep"))
            steep = 0;
         else if (p("--relax"))
            relax = 1;
         else if (p("--norelax"))
            relax = 0;
         else if (p("--nomip"))
            nomip = 1;
         else if (p("--first"))
            branch = BB_FIRST;
         else if (p("--last"))
            branch = BB_LAST;
         else if (p("--drtom"))
            branch = BB_DRTOM;
         else if (p("--fifo"))
            btrack = BB_FIFO;
         else if (p("--lifo"))
            btrack = BB_LIFO;
         else if (p("--bestp"))
            btrack = BB_BESTP;
         else if (argv[k][0] == '-' ||
                 (argv[k][0] == '-' && argv[k][1] == '-'))
         {  error("Invalid option `%s'; try %s --help", argv[k],
               argv[0]);
            exit(EXIT_FAILURE);
         }
         else
         {  if (in_fname != NULL)
            {  error("Only one input file allowed");
               exit(EXIT_FAILURE);
            }
            in_fname = argv[k];
         }
      }
      return;
}

/*----------------------------------------------------------------------
-- main - main routine.
--
-- This main routine is called by the control program and manages the
-- solving process. */

int main(int argc, char *argv[])
{     LPI *lp;
      clock_t t_start;
      /* process command line parameters */
      process_cmdline(argc, argv);
      /* read LP problem data from the input file */
      if (in_fname == NULL)
      {  error("No input file name specified; try %s --help", argv[0]);
         exit(EXIT_FAILURE);
      }
      switch (format)
      {  case 'M':
            lp = glp_read_mps1(in_fname);
            if (lp == NULL)
            {  error("MPS file processing error");
               exit(EXIT_FAILURE);
            }
            break;
         case 'L':
            lp = glp_read_lpm1(in_fname, fn_gener);
            if (lp == NULL)
            {  error("Model description processing error");
               exit(EXIT_FAILURE);
            }
            break;
         default:
            insist(format != format);
      }
      /* change optimization direction */
      if (obj_dir != 0) glp_set_obj_sense(lp, obj_dir);
      /* if only data check is required, skip computations */
      if (check) goto skip;
      /* solve LP/MIP problem */
      t_start = clock();
      switch (method)
      {  case 'O':
         case 'S':
            if (nomip || glp_get_num_int(lp) == 0)
            {  if (method == 'O')
               {  struct rsm1 parm;
                  glp_init_rsm1(&parm);
                  parm.what = option;
                  parm.scale = scale;
                  parm.dual = use_dual;
                  parm.steep = steep;
                  parm.relax = relax;
                  glp_call_rsm1(lp, &parm);
               }
               else
               {  struct spx2 parm;
                  glp_init_spx2(&parm);
                  parm.scale = scale;
                  glp_simplex2(lp, &parm);
               }
            }
            else
            {  struct bbm1 parm;
               glp_init_bbm1(&parm);
               parm.what = option;
               parm.branch = branch;
               parm.btrack = btrack;
               parm.meth = (method == 'O' ? 0 : 1);
               parm.scale = scale;
               parm.dual = use_dual;
               parm.steep = steep;
               parm.relax = relax;
               glp_call_bbm1(lp, &parm);
            }
            break;
         case 'I':
            if (glp_get_num_int(lp) > 0 && !nomip)
            {  error("Interior point solver is not able to solve MIP pr"
                  "oblem; use --simplex");
               exit(EXIT_FAILURE);
            }
            if (option == 0)
            {  error("Interior point solver is not able to compute init"
                  "ial basis solution; use --simplex");
               exit(EXIT_FAILURE);
            }
            /* if only primal feasible solution is required, nullify
               objective function coefficients */
            if (option == 1)
            {  int n = glp_get_num_cols(lp), j;
               for (j = 0; j <= n; j++) glp_set_obj_coef(lp, j, 0.0);
               glp_set_obj_sense(lp, '-');
            }
            glp_call_ipm1(lp);
            break;
         default:
            insist(method != method);
      }
      /* display statistics */
      print("Time used: %.1f secs",
         (double)(clock() - t_start) / (double)CLOCKS_PER_SEC);
      print("Memory used: %.1fM (%d bytes)",
         (double)get_env_ptr()->mem_tpeak / (double)1000000,
         get_env_ptr()->mem_tpeak);
      /* write problem solution found by the solver */
      if (out_fname != NULL && glp_print_soln(lp, out_fname) != 0)
      {  error("Unable to write problem solution");
         exit(EXIT_FAILURE);
      }
skip: /* delete the problem object */
      glp_delete_prob(lp);
#if 0
      print("%d %d",
         get_env_ptr()->mem_total, get_env_ptr()->mem_count);
#endif
      /* exit to the control program */
      return 0;
}

/* eof */
