/* glpsol.c (stand-alone GLPK LP/MIP solver) */

/***********************************************************************
*  This code is part of GLPK (GNU Linear Programming Kit).
*
*  Copyright (C) 2000-2016 Andrew Makhorin, Department for Applied
*  Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
*  reserved. E-mail: <mao@gnu.org>.
*
*  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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/

#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glpk.h>

#define xassert glp_assert
#define xerror  glp_error
#define xprintf glp_printf

struct csa
{     /* common storage area */
      glp_prob *prob;
      /* LP/MIP problem object */
      glp_bfcp bfcp;
      /* basis factorization control parameters */
      glp_smcp smcp;
      /* simplex method control parameters */
      glp_iptcp iptcp;
      /* interior-point method control parameters */
      glp_iocp iocp;
      /* integer optimizer control parameters */
      glp_tran *tran;
      /* model translator workspace */
      glp_graph *graph;
      /* network problem object */
      int format;
      /* problem file format: */
#define FMT_MPS_DECK    1  /* fixed MPS */
#define FMT_MPS_FILE    2  /* free MPS */
#define FMT_LP          3  /* CPLEX LP */
#define FMT_GLP         4  /* GLPK LP/MIP */
#define FMT_MATHPROG    5  /* MathProg */
#define FMT_MIN_COST    6  /* DIMACS min-cost flow */
#define FMT_MAX_FLOW    7  /* DIMACS maximum flow */
#if 1 /* 06/VIII-2011 */
#define FMT_CNF         8  /* DIMACS CNF-SAT */
#endif
      const char *in_file;
      /* name of input problem file */
#define DATA_MAX 10
      /* maximal number of input data files */
      int ndf;
      /* number of input data files specified */
      const char *in_data[1+DATA_MAX];
      /* name(s) of input data file(s) */
      const char *out_dpy;
      /* name of output file to send display output; NULL means the
         display output is sent to the terminal */
      int seed;
      /* seed value to be passed to the MathProg translator; initially
         set to 1; 0x80000000 means the value is omitted */
      int solution;
      /* solution type flag: */
#define SOL_BASIC       1  /* basic */
#define SOL_INTERIOR    2  /* interior-point */
#define SOL_INTEGER     3  /* mixed integer */
      const char *in_res;
      /* name of input solution file in raw format */
      int dir;
      /* optimization direction flag:
         0       - not specified
         GLP_MIN - minimization
         GLP_MAX - maximization */
      int scale;
      /* automatic problem scaling flag */
      const char *out_sol;
      /* name of output solution file in printable format */
      const char *out_res;
      /* name of output solution file in raw format */
      const char *out_ranges;
      /* name of output file to write sensitivity analysis report */
      int check;
      /* input data checking flag; no solution is performed */
      const char *new_name;
      /* new name to be assigned to the problem */
      const char *out_mps;
      /* name of output problem file in fixed MPS format */
      const char *out_freemps;
      /* name of output problem file in free MPS format */
      const char *out_cpxlp;
      /* name of output problem file in CPLEX LP format */
      const char *out_glp;
      /* name of output problem file in GLPK format */
#if 0
      const char *out_pb;
      /* name of output problem file in OPB format */
      const char *out_npb;
      /* name of output problem file in normalized OPB format */
#endif
#if 1 /* 06/VIII-2011 */
      const char *out_cnf;
      /* name of output problem file in DIMACS CNF-SAT format */
#endif
      const char *log_file;
      /* name of output file to hardcopy terminal output */
      int crash;
      /* initial basis option: */
#define USE_STD_BASIS   1  /* use standard basis */
#define USE_ADV_BASIS   2  /* use advanced basis */
#define USE_CPX_BASIS   3  /* use Bixby's basis */
#define USE_INI_BASIS   4  /* use initial basis from ini_file */
      const char *ini_file;
      /* name of input file containing initial basis */
      int exact;
      /* flag to use glp_exact rather than glp_simplex */
      int xcheck;
      /* flag to check final basis with glp_exact */
      int nomip;
      /* flag to consider MIP as pure LP */
#if 1 /* 15/VIII-2011 */
      int minisat;
      /* option to solve feasibility problem with MiniSat solver */
      int use_bnd;
      /* option to bound objective function */
      int obj_bnd;
      /* upper (minization) or lower (maximization) objective bound */
#endif
#if 1 /* 11/VII-2013 */
      const char *use_sol;
      /* name of input mip solution file in GLPK format */
#endif
};

static int str2int(const char *s, int *x)
{     /* convert string to integer */
      long t;
      char *endptr;
      t = strtol(s, &endptr, 10);
      if (*endptr != '\0')
         return 2;
      if (!(INT_MIN <= t && t <= INT_MAX))
         return 1;
      *x = t;
#if 0
      xprintf("str2int: x = %d\n", *x);
#endif
      return 0;
}

static int str2num(const char *s, double *x)
{     /* convert string to floating point */
      double t;
      char *endptr;
      t = strtod(s, &endptr);
      if (*endptr != '\0')
         return 2;
      if (!(-DBL_MAX <= t && t <= +DBL_MAX))
         return 1;
      *x = t;
#if 0
      xprintf("str2num: x = %g\n", *x);
#endif
      return 0;
}

static void print_help(const char *my_name)
{     /* print help information */
      xprintf("Usage: %s [options...] filename\n", my_name);
      xprintf("\n");
      xprintf("General options:\n");
      xprintf("   --mps             read LP/MIP problem in fixed MPS fo"
         "rmat\n");
      xprintf("   --freemps         read LP/MIP problem in free MPS for"
         "mat (default)\n");
      xprintf("   --lp              read LP/MIP problem in CPLEX LP for"
         "mat\n");
      xprintf("   --glp             read LP/MIP problem in GLPK format "
         "\n");
      xprintf("   --math            read LP/MIP model written in GNU Ma"
         "thProg modeling\n");
      xprintf("                     language\n");
      xprintf("   -m filename, --model filename\n");
      xprintf("                     read model section and optional dat"
         "a section from\n");
      xprintf("                     filename (same as --math)\n");
      xprintf("   -d filename, --data filename\n");
      xprintf("                     read data section from filename (fo"
         "r --math only);\n");
      xprintf("                     if model file also has data section"
         ", it is ignored\n");
      xprintf("   -y filename, --display filename\n");
      xprintf("                     send display output to filename (fo"
         "r --math only);\n");
      xprintf("                     by default the output is sent to te"
         "rminal\n");
      xprintf("   --seed value      initialize pseudo-random number gen"
         "erator used in\n");
      xprintf("                     MathProg model with specified seed "
         "(any integer);\n");
      xprintf("                     if seed value is ?, some random see"
         "d will be used\n");
      xprintf("   --mincost         read min-cost flow problem in DIMAC"
         "S format\n");
      xprintf("   --maxflow         read maximum flow problem in DIMACS"
         " format\n");
#if 1 /* 06/VIII-2011 */
      xprintf("   --cnf             read CNF-SAT problem in DIMACS form"
         "at\n");
#endif
      xprintf("   --simplex         use simplex method (default)\n");
      xprintf("   --interior        use interior point method (LP only)"
         "\n");
      xprintf("   -r filename, --read filename\n");
      xprintf("                     read solution from filename rather "
         "to find it with\n");
      xprintf("                     the solver\n");
      xprintf("   --min             minimization\n");
      xprintf("   --max             maximization\n");
      xprintf("   --scale           scale problem (default)\n");
      xprintf("   --noscale         do not scale problem\n");
      xprintf("   -o filename, --output filename\n");
      xprintf("                     write solution to filename in print"
         "able format\n");
      xprintf("   -w filename, --write filename\n");
      xprintf("                     write solution to filename in plain"
         " text format\n");
      xprintf("   --ranges filename\n");
      xprintf("                     write sensitivity analysis report t"
         "o filename in\n");
      xprintf("                     printable format (simplex only)\n");
      xprintf("   --tmlim nnn       limit solution time to nnn seconds "
         "\n");
      xprintf("   --memlim nnn      limit available memory to nnn megab"
         "ytes\n");
      xprintf("   --check           do not solve problem, check input d"
         "ata only\n");
      xprintf("   --name probname   change problem name to probname\n");
      xprintf("   --wmps filename   write problem to filename in fixed "
         "MPS format\n");
      xprintf("   --wfreemps filename\n");
      xprintf("                     write problem to filename in free M"
         "PS format\n");
      xprintf("   --wlp filename    write problem to filename in CPLEX "
         "LP format\n");
      xprintf("   --wglp filename   write problem to filename in GLPK f"
         "ormat\n");
#if 0
      xprintf("   --wpb filename    write problem to filename in OPB fo"
         "rmat\n");
      xprintf("   --wnpb filename   write problem to filename in normal"
         "ized OPB format\n");
#endif
#if 1 /* 06/VIII-2011 */
      xprintf("   --wcnf filename   write problem to filename in DIMACS"
         " CNF-SAT format\n");
#endif
      xprintf("   --log filename    write copy of terminal output to fi"
         "lename\n");
      xprintf("   -h, --help        display this help information and e"
         "xit\n");
      xprintf("   -v, --version     display program version and exit\n")
         ;
      xprintf("\n");
      xprintf("LP basis factorization options:\n");
#if 0 /* 08/III-2014 */
      xprintf("   --luf             LU + Forrest-Tomlin update\n");
      xprintf("                     (faster, less stable; default)\n");
      xprintf("   --cbg             LU + Schur complement + Bartels-Gol"
         "ub update\n");
      xprintf("                     (slower, more stable)\n");
      xprintf("   --cgr             LU + Schur complement + Givens rota"
         "tion update\n");
      xprintf("                     (slower, more stable)\n");
#else
      xprintf("   --luf             plain LU-factorization (default)\n")
         ;
      xprintf("   --btf             block triangular LU-factorization\n"
         );
      xprintf("   --ft              Forrest-Tomlin update (requires --l"
         "uf; default)\n");
      xprintf("   --cbg             Schur complement + Bartels-Golub up"
         "date\n");
      xprintf("   --cgr             Schur complement + Givens rotation "
         "update\n");
#endif
      xprintf("\n");
      xprintf("Options specific to simplex solver:\n");
      xprintf("   --primal          use primal simplex (default)\n");
      xprintf("   --dual            use dual simplex\n");
      xprintf("   --std             use standard initial basis of all s"
         "lacks\n");
      xprintf("   --adv             use advanced initial basis (default"
         ")\n");
      xprintf("   --bib             use Bixby's initial basis\n");
      xprintf("   --ini filename    use as initial basis previously sav"
         "ed with -w\n");
      xprintf("                     (disables LP presolver)\n");
      xprintf("   --steep           use steepest edge technique (defaul"
         "t)\n");
      xprintf("   --nosteep         use standard \"textbook\" pricing\n"
         );
      xprintf("   --relax           use Harris' two-pass ratio test (de"
         "fault)\n");
      xprintf("   --norelax         use standard \"textbook\" ratio tes"
         "t\n");
#if 1 /* 28/III-2016 */
      xprintf("   --flip            use flip-flop ratio test (assumes -"
         "-dual)\n");
#endif
      xprintf("   --presol          use presolver (default; assumes --s"
         "cale and --adv)\n");
      xprintf("   --nopresol        do not use presolver\n");
      xprintf("   --exact           use simplex method based on exact a"
         "rithmetic\n");
      xprintf("   --xcheck          check final basis using exact arith"
         "metic\n");
      xprintf("\n");
      xprintf("Options specific to interior-point solver:\n");
      xprintf("   --nord            use natural (original) ordering\n");
      xprintf("   --qmd             use quotient minimum degree orderin"
         "g\n");
      xprintf("   --amd             use approximate minimum degree orde"
         "ring (default)\n");
      xprintf("   --symamd          use approximate minimum degree orde"
         "ring\n");
      xprintf("\n");
      xprintf("Options specific to MIP solver:\n");
      xprintf("   --nomip           consider all integer variables as c"
         "ontinuous\n");
      xprintf("                     (allows solving MIP as pure LP)\n");
      xprintf("   --first           branch on first integer variable\n")
         ;
      xprintf("   --last            branch on last integer variable\n");
      xprintf("   --mostf           branch on most fractional variable "
         "\n");
      xprintf("   --drtom           branch using heuristic by Driebeck "
         "and Tomlin\n");
      xprintf("                     (default)\n");
      xprintf("   --pcost           branch using hybrid pseudocost heur"
         "istic (may be\n");
      xprintf("                     useful for hard instances)\n");
      xprintf("   --dfs             backtrack using depth first search "
         "\n");
      xprintf("   --bfs             backtrack using breadth first searc"
         "h\n");
      xprintf("   --bestp           backtrack using the best projection"
         " heuristic\n");
      xprintf("   --bestb           backtrack using node with best loca"
         "l bound\n");
      xprintf("                     (default)\n");
      xprintf("   --intopt          use MIP presolver (default)\n");
      xprintf("   --nointopt        do not use MIP presolver\n");
      xprintf("   --binarize        replace general integer variables b"
         "y binary ones\n");
      xprintf("                     (assumes --intopt)\n");
      xprintf("   --fpump           apply feasibility pump heuristic\n")
         ;
#if 1 /* 29/VI-2013 */
      xprintf("   --proxy [nnn]     apply proximity search heuristic (n"
         "nn is time limit\n");
      xprintf("                     in seconds; default is 60)\n");
#endif
      xprintf("   --gomory          generate Gomory's mixed integer cut"
         "s\n");
      xprintf("   --mir             generate MIR (mixed integer roundin"
         "g) cuts\n");
      xprintf("   --cover           generate mixed cover cuts\n");
      xprintf("   --clique          generate clique cuts\n");
      xprintf("   --cuts            generate all cuts above\n");
      xprintf("   --mipgap tol      set relative mip gap tolerance to t"
         "ol\n");
#if 1 /* 15/VIII-2011 */
      xprintf("   --minisat         translate integer feasibility probl"
         "em to CNF-SAT\n");
      xprintf("                     and solve it with MiniSat solver\n")
         ;
      xprintf("   --objbnd bound    add inequality obj <= bound (minimi"
         "zation) or\n");
      xprintf("                     obj >= bound (maximization) to inte"
         "ger feasibility\n");
      xprintf("                     problem (assumes --minisat)\n");
#endif
      xprintf("\n");
      xprintf("For description of the MPS and CPLEX LP formats see Refe"
         "rence Manual.\n");
      xprintf("For description of the modeling language see \"GLPK: Mod"
         "eling Language\n");
      xprintf("GNU MathProg\". Both documents are included in the GLPK "
         "distribution.\n");
      xprintf("\n");
      xprintf("See GLPK web page at <http://www.gnu.org/software/glpk/g"
         "lpk.html>.\n");
      xprintf("\n");
      xprintf("Please report bugs to <bug-glpk@gnu.org>.\n");
      return;
}

static void print_version(int briefly)
{     /* print version information */
      xprintf("GLPSOL: GLPK LP/MIP Solver, v%s\n", glp_version());
      if (briefly) goto done;
      xprintf("\n");
      xprintf("Copyright (C) 2000-2015 Andrew Makhorin, Department for "
         "Applied\n");
      xprintf("Informatics, Moscow Aviation Institute, Moscow, Russia. "
         "All rights\n");
      xprintf("reserved. E-mail: <mao@gnu.org>.\n");
      xprintf("\n");
      xprintf("This program has ABSOLUTELY NO WARRANTY.\n");
      xprintf("\n");
      xprintf("This program is free software; you may re-distribute it "
         "under the terms\n");
      xprintf("of the GNU General Public License version 3 or later.\n")
         ;
done: return;
}

static int parse_cmdline(struct csa *csa, int argc, char *argv[])
{     /* parse command-line parameters */
      int k;
#define p(str) (strcmp(argv[k], str) == 0)
      for (k = 1; k < argc; k++)
      {  if (p("--mps"))
            csa->format = FMT_MPS_DECK;
         else if (p("--freemps"))
            csa->format = FMT_MPS_FILE;
         else if (p("--lp") || p("--cpxlp"))
            csa->format = FMT_LP;
         else if (p("--glp"))
            csa->format = FMT_GLP;
         else if (p("--math") || p("-m") || p("--model"))
            csa->format = FMT_MATHPROG;
         else if (p("-d") || p("--data"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No input data file specified\n");
               return 1;
            }
            if (csa->ndf == DATA_MAX)
            {  xprintf("Too many input data files\n");
               return 1;
            }
            csa->in_data[++(csa->ndf)] = argv[k];
         }
         else if (p("-y") || p("--display"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No display output file specified\n");
               return 1;
            }
            if (csa->out_dpy != NULL)
            {  xprintf("Only one display output file allowed\n");
               return 1;
            }
            csa->out_dpy = argv[k];
         }
         else if (p("--seed"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' ||
               argv[k][0] == '-' && !isdigit((unsigned char)argv[k][1]))
            {  xprintf("No seed value specified\n");
               return 1;
            }
            if (strcmp(argv[k], "?") == 0)
               csa->seed = 0x80000000;
            else if (str2int(argv[k], &csa->seed))
            {  xprintf("Invalid seed value '%s'\n", argv[k]);
               return 1;
            }
         }
         else if (p("--mincost"))
            csa->format = FMT_MIN_COST;
         else if (p("--maxflow"))
            csa->format = FMT_MAX_FLOW;
#if 1 /* 06/VIII-2011 */
         else if (p("--cnf"))
            csa->format = FMT_CNF;
#endif
         else if (p("--simplex"))
            csa->solution = SOL_BASIC;
         else if (p("--interior"))
            csa->solution = SOL_INTERIOR;
#if 1 /* 28/V-2010 */
         else if (p("--alien"))
            csa->iocp.alien = GLP_ON;
#endif
         else if (p("-r") || p("--read"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No input solution file specified\n");
               return 1;
            }
            if (csa->in_res != NULL)
            {  xprintf("Only one input solution file allowed\n");
               return 1;
            }
            csa->in_res = argv[k];
         }
         else if (p("--min"))
            csa->dir = GLP_MIN;
         else if (p("--max"))
            csa->dir = GLP_MAX;
         else if (p("--scale"))
            csa->scale = 1;
         else if (p("--noscale"))
            csa->scale = 0;
         else if (p("-o") || p("--output"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No output solution file specified\n");
               return 1;
            }
            if (csa->out_sol != NULL)
            {  xprintf("Only one output solution file allowed\n");
               return 1;
            }
            csa->out_sol = argv[k];
         }
         else if (p("-w") || p("--write"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No output solution file specified\n");
               return 1;
            }
            if (csa->out_res != NULL)
            {  xprintf("Only one output solution file allowed\n");
               return 1;
            }
            csa->out_res = argv[k];
         }
         else if (p("--ranges") || p("--bounds"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No output file specified to write sensitivity a"
                  "nalysis report\n");
               return 1;
            }
            if (csa->out_ranges != NULL)
            {  xprintf("Only one output file allowed to write sensitivi"
                  "ty analysis report\n");
               return 1;
            }
            csa->out_ranges = argv[k];
         }
         else if (p("--tmlim"))
         {  int tm_lim;
            k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No time limit specified\n");
               return 1;
            }
            if (str2int(argv[k], &tm_lim) || tm_lim < 0)
            {  xprintf("Invalid time limit '%s'\n", argv[k]);
               return 1;
            }
            if (tm_lim <= INT_MAX / 1000)
               csa->smcp.tm_lim = csa->iocp.tm_lim = 1000 * tm_lim;
            else
               csa->smcp.tm_lim = csa->iocp.tm_lim = INT_MAX;
         }
         else if (p("--memlim"))
         {  int mem_lim;
            k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No memory limit specified\n");
               return 1;
            }
            if (str2int(argv[k], &mem_lim) || mem_lim < 1)
            {  xprintf("Invalid memory limit '%s'\n", argv[k]);
               return 1;
            }
            glp_mem_limit(mem_lim);
         }
         else if (p("--check"))
            csa->check = 1;
         else if (p("--name"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No problem name specified\n");
               return 1;
            }
            if (csa->new_name != NULL)
            {  xprintf("Only one problem name allowed\n");
               return 1;
            }
            csa->new_name = argv[k];
         }
         else if (p("--wmps"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No fixed MPS output file specified\n");
               return 1;
            }
            if (csa->out_mps != NULL)
            {  xprintf("Only one fixed MPS output file allowed\n");
               return 1;
            }
            csa->out_mps = argv[k];
         }
         else if (p("--wfreemps"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No free MPS output file specified\n");
               return 1;
            }
            if (csa->out_freemps != NULL)
            {  xprintf("Only one free MPS output file allowed\n");
               return 1;
            }
            csa->out_freemps = argv[k];
         }
         else if (p("--wlp") || p("--wcpxlp") || p("--wlpt"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No CPLEX LP output file specified\n");
               return 1;
            }
            if (csa->out_cpxlp != NULL)
            {  xprintf("Only one CPLEX LP output file allowed\n");
               return 1;
            }
            csa->out_cpxlp = argv[k];
         }
         else if (p("--wglp"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No GLPK LP/MIP output file specified\n");
               return 1;
            }
            if (csa->out_glp != NULL)
            {  xprintf("Only one GLPK LP/MIP output file allowed\n");
               return 1;
            }
            csa->out_glp = argv[k];
         }
#if 0
         else if (p("--wpb"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No problem output file specified\n");
               return 1;
            }
            if (csa->out_pb != NULL)
            {  xprintf("Only one OPB output file allowed\n");
               return 1;
            }
            csa->out_pb = argv[k];
         }
         else if (p("--wnpb"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No problem output file specified\n");
               return 1;
            }
            if (csa->out_npb != NULL)
            {  xprintf("Only one normalized OPB output file allowed\n");
               return 1;
            }
            csa->out_npb = argv[k];
         }
#endif
#if 1 /* 06/VIII-2011 */
         else if (p("--wcnf"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No problem output file specified\n");
               return 1;
            }
            if (csa->out_cnf != NULL)
            {  xprintf("Only one output DIMACS CNF-SAT file allowed\n");
               return 1;
            }
            csa->out_cnf = argv[k];
         }
#endif
         else if (p("--log"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No log file specified\n");
               return 1;
            }
            if (csa->log_file != NULL)
            {  xprintf("Only one log file allowed\n");
               return 1;
            }
            csa->log_file = argv[k];
         }
         else if (p("-h") || p("--help"))
         {  print_help(argv[0]);
            return -1;
         }
         else if (p("-v") || p("--version"))
         {  print_version(0);
            return -1;
         }
#if 0 /* 08/III-2014 */
         else if (p("--luf"))
            csa->bfcp.type = GLP_BF_FT;
         else if (p("--cbg"))
            csa->bfcp.type = GLP_BF_BG;
         else if (p("--cgr"))
            csa->bfcp.type = GLP_BF_GR;
#else
         else if (p("--luf"))
         {  csa->bfcp.type &= 0x0F;
            csa->bfcp.type |= GLP_BF_LUF;
         }
         else if (p("--btf"))
         {  csa->bfcp.type &= 0x0F;
            csa->bfcp.type |= GLP_BF_BTF;
         }
         else if (p("--ft"))
         {  csa->bfcp.type &= 0xF0;
            csa->bfcp.type |= GLP_BF_FT;
         }
         else if (p("--cbg"))
         {  csa->bfcp.type &= 0xF0;
            csa->bfcp.type |= GLP_BF_BG;
         }
         else if (p("--cgr"))
         {  csa->bfcp.type &= 0xF0;
            csa->bfcp.type |= GLP_BF_GR;
         }
#endif
         else if (p("--primal"))
            csa->smcp.meth = GLP_PRIMAL;
         else if (p("--dual"))
            csa->smcp.meth = GLP_DUAL;
         else if (p("--std"))
            csa->crash = USE_STD_BASIS;
         else if (p("--adv"))
            csa->crash = USE_ADV_BASIS;
         else if (p("--bib"))
            csa->crash = USE_CPX_BASIS;
         else if (p("--ini"))
         {  csa->crash = USE_INI_BASIS;
            csa->smcp.presolve = GLP_OFF;
            k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No initial basis file specified\n");
               return 1;
            }
            if (csa->ini_file != NULL)
            {  xprintf("Only one initial basis file allowed\n");
               return 1;
            }
            csa->ini_file = argv[k];
         }
         else if (p("--steep"))
            csa->smcp.pricing = GLP_PT_PSE;
         else if (p("--nosteep"))
            csa->smcp.pricing = GLP_PT_STD;
         else if (p("--relax"))
            csa->smcp.r_test = GLP_RT_HAR;
         else if (p("--norelax"))
            csa->smcp.r_test = GLP_RT_STD;
#if 1 /* 28/III-2016 */
         else if (p("--flip"))
         {  csa->smcp.meth = GLP_DUAL;
            csa->smcp.r_test = GLP_RT_FLIP;
            csa->iocp.flip = GLP_ON;
         }
#endif
         else if (p("--presol"))
            csa->smcp.presolve = GLP_ON;
         else if (p("--nopresol"))
            csa->smcp.presolve = GLP_OFF;
         else if (p("--exact"))
            csa->exact = 1;
         else if (p("--xcheck"))
            csa->xcheck = 1;
         else if (p("--nord"))
            csa->iptcp.ord_alg = GLP_ORD_NONE;
         else if (p("--qmd"))
            csa->iptcp.ord_alg = GLP_ORD_QMD;
         else if (p("--amd"))
            csa->iptcp.ord_alg = GLP_ORD_AMD;
         else if (p("--symamd"))
            csa->iptcp.ord_alg = GLP_ORD_SYMAMD;
         else if (p("--nomip"))
            csa->nomip = 1;
         else if (p("--first"))
            csa->iocp.br_tech = GLP_BR_FFV;
         else if (p("--last"))
            csa->iocp.br_tech = GLP_BR_LFV;
         else if (p("--drtom"))
            csa->iocp.br_tech = GLP_BR_DTH;
         else if (p("--mostf"))
            csa->iocp.br_tech = GLP_BR_MFV;
         else if (p("--pcost"))
            csa->iocp.br_tech = GLP_BR_PCH;
         else if (p("--dfs"))
            csa->iocp.bt_tech = GLP_BT_DFS;
         else if (p("--bfs"))
            csa->iocp.bt_tech = GLP_BT_BFS;
         else if (p("--bestp"))
            csa->iocp.bt_tech = GLP_BT_BPH;
         else if (p("--bestb"))
            csa->iocp.bt_tech = GLP_BT_BLB;
         else if (p("--intopt"))
            csa->iocp.presolve = GLP_ON;
         else if (p("--nointopt"))
            csa->iocp.presolve = GLP_OFF;
         else if (p("--binarize"))
            csa->iocp.presolve = csa->iocp.binarize = GLP_ON;
         else if (p("--fpump"))
            csa->iocp.fp_heur = GLP_ON;
#if 1 /* 29/VI-2013 */
         else if (p("--proxy"))
         {  csa->iocp.ps_heur = GLP_ON;
            if (argv[k+1] && isdigit((unsigned char)argv[k+1][0]))
            {  int nnn;
               k++;
               if (str2int(argv[k], &nnn) || nnn < 1)
               {  xprintf("Invalid proxy time limit '%s'\n", argv[k]);
                  return 1;
               }
               csa->iocp.ps_tm_lim = 1000 * nnn;
            }
         }
#endif
         else if (p("--gomory"))
            csa->iocp.gmi_cuts = GLP_ON;
         else if (p("--mir"))
            csa->iocp.mir_cuts = GLP_ON;
         else if (p("--cover"))
            csa->iocp.cov_cuts = GLP_ON;
         else if (p("--clique"))
            csa->iocp.clq_cuts = GLP_ON;
         else if (p("--cuts"))
            csa->iocp.gmi_cuts = csa->iocp.mir_cuts =
            csa->iocp.cov_cuts = csa->iocp.clq_cuts = GLP_ON;
         else if (p("--mipgap"))
         {  double mip_gap;
            k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No relative gap tolerance specified\n");
               return 1;
            }
            if (str2num(argv[k], &mip_gap) || mip_gap < 0.0)
            {  xprintf("Invalid relative mip gap tolerance '%s'\n",
                  argv[k]);
               return 1;
            }
            csa->iocp.mip_gap = mip_gap;
         }
#if 1 /* 15/VIII-2011 */
         else if (p("--minisat"))
            csa->minisat = 1;
         else if (p("--objbnd"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' ||
               argv[k][0] == '-' && !isdigit((unsigned char)argv[k][1]))
            {  xprintf("No objective bound specified\n");
               return 1;
            }
            csa->minisat = 1;
            csa->use_bnd = 1;
            if (str2int(argv[k], &csa->obj_bnd))
            {  xprintf("Invalid objective bound '%s' (should be integer"
                  " value)\n", argv[k]);
               return 1;
            }
         }
#endif
#if 1 /* 11/VII-2013 */
         else if (p("--use"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No input MIP solution file specified\n");
               return 1;
            }
            if (csa->use_sol != NULL)
            {  xprintf("Only one input MIP solution file allowed\n");
               return 1;
            }
            csa->use_sol = argv[k];
         }
         else if (p("--save"))
         {  k++;
            if (k == argc || argv[k][0] == '\0' || argv[k][0] == '-')
            {  xprintf("No output MIP solution file specified\n");
               return 1;
            }
            if (csa->iocp.save_sol != NULL)
            {  xprintf("Only one output MIP solution file allowed\n");
               return 1;
            }
            csa->iocp.save_sol = argv[k];
         }
#endif
         else if (argv[k][0] == '-' ||
                 (argv[k][0] == '-' && argv[k][1] == '-'))
         {  xprintf("Invalid option '%s'; try %s --help\n",
               argv[k], argv[0]);
            return 1;
         }
         else
         {  if (csa->in_file != NULL)
            {  xprintf("Only one input problem file allowed\n");
               return 1;
            }
            csa->in_file = argv[k];
         }
      }
#undef p
      return 0;
}

typedef struct { double rhs, pi; } v_data;
typedef struct { double low, cap, cost, x; } a_data;

int main(int argc, char *argv[])
{     /* stand-alone LP/MIP solver */
      struct csa _csa, *csa = &_csa;
      int ret;
#if 0 /* 10/VI-2013 */
      glp_long start;
#else
      double start;
#endif
      /* perform initialization */
      csa->prob = glp_create_prob();
      glp_get_bfcp(csa->prob, &csa->bfcp);
      glp_init_smcp(&csa->smcp);
      csa->smcp.presolve = GLP_ON;
      glp_init_iptcp(&csa->iptcp);
      glp_init_iocp(&csa->iocp);
      csa->iocp.presolve = GLP_ON;
      csa->tran = NULL;
      csa->graph = NULL;
      csa->format = FMT_MPS_FILE;
      csa->in_file = NULL;
      csa->ndf = 0;
      csa->out_dpy = NULL;
      csa->seed = 1;
      csa->solution = SOL_BASIC;
      csa->in_res = NULL;
      csa->dir = 0;
      csa->scale = 1;
      csa->out_sol = NULL;
      csa->out_res = NULL;
      csa->out_ranges = NULL;
      csa->check = 0;
      csa->new_name = NULL;
      csa->out_mps = NULL;
      csa->out_freemps = NULL;
      csa->out_cpxlp = NULL;
      csa->out_glp = NULL;
#if 0
      csa->out_pb = NULL;
      csa->out_npb = NULL;
#endif
#if 1 /* 06/VIII-2011 */
      csa->out_cnf = NULL;
#endif
      csa->log_file = NULL;
      csa->crash = USE_ADV_BASIS;
      csa->ini_file = NULL;
      csa->exact = 0;
      csa->xcheck = 0;
      csa->nomip = 0;
#if 1 /* 15/VIII-2011 */
      csa->minisat = 0;
      csa->use_bnd = 0;
      csa->obj_bnd = 0;
#endif
#if 1 /* 11/VII-2013 */
      csa->use_sol = NULL;
#endif
      /* parse command-line parameters */
      ret = parse_cmdline(csa, argc, argv);
      if (ret < 0)
      {  ret = EXIT_SUCCESS;
         goto done;
      }
      if (ret > 0)
      {  ret = EXIT_FAILURE;
         goto done;
      }
      /*--------------------------------------------------------------*/
      /* remove all output files specified in the command line */
      if (csa->out_dpy != NULL) remove(csa->out_dpy);
      if (csa->out_sol != NULL) remove(csa->out_sol);
      if (csa->out_res != NULL) remove(csa->out_res);
      if (csa->out_ranges != NULL) remove(csa->out_ranges);
      if (csa->out_mps != NULL) remove(csa->out_mps);
      if (csa->out_freemps != NULL) remove(csa->out_freemps);
      if (csa->out_cpxlp != NULL) remove(csa->out_cpxlp);
      if (csa->out_glp != NULL) remove(csa->out_glp);
#if 0
      if (csa->out_pb != NULL) remove(csa->out_pb);
      if (csa->out_npb != NULL) remove(csa->out_npb);
#endif
#if 1 /* 06/VIII-2011 */
      if (csa->out_cnf != NULL) remove(csa->out_cnf);
#endif
      if (csa->log_file != NULL) remove(csa->log_file);
      /*--------------------------------------------------------------*/
      /* open log file, if required */
      if (csa->log_file != NULL)
      {  if (glp_open_tee(csa->log_file))
         {  xprintf("Unable to create log file\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /*--------------------------------------------------------------*/
      /* print version information */
      print_version(1);
      /*--------------------------------------------------------------*/
      /* print parameters specified in the command line */
      if (argc > 1)
      {  int k, len = INT_MAX;
         xprintf("Parameter(s) specified in the command line:");
         for (k = 1; k < argc; k++)
         {  if (len > 72)
               xprintf("\n"), len = 0;
            xprintf(" %s", argv[k]);
            len += 1 + strlen(argv[k]);
         }
         xprintf("\n");
      }
      /*--------------------------------------------------------------*/
      /* read problem data from the input file */
      if (csa->in_file == NULL)
      {  xprintf("No input problem file specified; try %s --help\n",
            argv[0]);
         ret = EXIT_FAILURE;
         goto done;
      }
      if (csa->format == FMT_MPS_DECK)
      {  ret = glp_read_mps(csa->prob, GLP_MPS_DECK, NULL,
            csa->in_file);
         if (ret != 0)
err1:    {  xprintf("MPS file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      else if (csa->format == FMT_MPS_FILE)
      {  ret = glp_read_mps(csa->prob, GLP_MPS_FILE, NULL,
            csa->in_file);
         if (ret != 0) goto err1;
      }
      else if (csa->format == FMT_LP)
      {  ret = glp_read_lp(csa->prob, NULL, csa->in_file);
         if (ret != 0)
         {  xprintf("CPLEX LP file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      else if (csa->format == FMT_GLP)
      {  ret = glp_read_prob(csa->prob, 0, csa->in_file);
         if (ret != 0)
         {  xprintf("GLPK LP/MIP file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      else if (csa->format == FMT_MATHPROG)
      {  int k;
         /* allocate the translator workspace */
         csa->tran = glp_mpl_alloc_wksp();
         /* set seed value */
         if (csa->seed == 0x80000000)
#if 0 /* 10/VI-2013 */
         {  csa->seed = glp_time().lo;
#else
         {  csa->seed = (int)fmod(glp_time(), 1000000000.0);
#endif
            xprintf("Seed value %d will be used\n", csa->seed);
         }
         glp_mpl_init_rand(csa->tran, csa->seed);
         /* read model section and optional data section */
         if (glp_mpl_read_model(csa->tran, csa->in_file, csa->ndf > 0))
err2:    {  xprintf("MathProg model processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         /* read optional data section(s), if necessary */
         for (k = 1; k <= csa->ndf; k++)
         {  if (glp_mpl_read_data(csa->tran, csa->in_data[k]))
               goto err2;
         }
         /* generate the model */
         if (glp_mpl_generate(csa->tran, csa->out_dpy)) goto err2;
         /* build the problem instance from the model */
         glp_mpl_build_prob(csa->tran, csa->prob);
      }
      else if (csa->format == FMT_MIN_COST)
      {  csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data));
         ret = glp_read_mincost(csa->graph, offsetof(v_data, rhs),
            offsetof(a_data, low), offsetof(a_data, cap),
            offsetof(a_data, cost), csa->in_file);
         if (ret != 0)
         {  xprintf("DIMACS file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         glp_mincost_lp(csa->prob, csa->graph, GLP_ON,
            offsetof(v_data, rhs), offsetof(a_data, low),
            offsetof(a_data, cap), offsetof(a_data, cost));
         glp_set_prob_name(csa->prob, csa->in_file);
      }
      else if (csa->format == FMT_MAX_FLOW)
      {  int s, t;
         csa->graph = glp_create_graph(sizeof(v_data), sizeof(a_data));
         ret = glp_read_maxflow(csa->graph, &s, &t,
            offsetof(a_data, cap), csa->in_file);
         if (ret != 0)
         {  xprintf("DIMACS file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         glp_maxflow_lp(csa->prob, csa->graph, GLP_ON, s, t,
            offsetof(a_data, cap));
         glp_set_prob_name(csa->prob, csa->in_file);
      }
#if 1 /* 06/VIII-2011 */
      else if (csa->format == FMT_CNF)
      {  ret = glp_read_cnfsat(csa->prob, csa->in_file);
         if (ret != 0)
         {  xprintf("DIMACS file processing error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         glp_set_prob_name(csa->prob, csa->in_file);
      }
#endif
      else
         xassert(csa != csa);
      /*--------------------------------------------------------------*/
      /* change problem name, if required */
      if (csa->new_name != NULL)
         glp_set_prob_name(csa->prob, csa->new_name);
      /* change optimization direction, if required */
      if (csa->dir != 0)
         glp_set_obj_dir(csa->prob, csa->dir);
      /* sort elements of the constraint matrix */
      glp_sort_matrix(csa->prob);
      /*--------------------------------------------------------------*/
      /* write problem data in fixed MPS format, if required */
      if (csa->out_mps != NULL)
      {  ret = glp_write_mps(csa->prob, GLP_MPS_DECK, NULL,
            csa->out_mps);
         if (ret != 0)
         {  xprintf("Unable to write problem in fixed MPS format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write problem data in free MPS format, if required */
      if (csa->out_freemps != NULL)
      {  ret = glp_write_mps(csa->prob, GLP_MPS_FILE, NULL,
            csa->out_freemps);
         if (ret != 0)
         {  xprintf("Unable to write problem in free MPS format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write problem data in CPLEX LP format, if required */
      if (csa->out_cpxlp != NULL)
      {  ret = glp_write_lp(csa->prob, NULL, csa->out_cpxlp);
         if (ret != 0)
         {  xprintf("Unable to write problem in CPLEX LP format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write problem data in GLPK format, if required */
      if (csa->out_glp != NULL)
      {  ret = glp_write_prob(csa->prob, 0, csa->out_glp);
         if (ret != 0)
         {  xprintf("Unable to write problem in GLPK format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
#if 0
      /* write problem data in OPB format, if required */
      if (csa->out_pb != NULL)
      {  ret = lpx_write_pb(csa->prob, csa->out_pb, 0, 0);
         if (ret != 0)
         {  xprintf("Unable to write problem in OPB format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write problem data in normalized OPB format, if required */
      if (csa->out_npb != NULL)
      {  ret = lpx_write_pb(csa->prob, csa->out_npb, 1, 1);
         if (ret != 0)
         {  xprintf(
               "Unable to write problem in normalized OPB format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
#endif
#if 1 /* 06/VIII-2011 */
      /* write problem data in DIMACS CNF-SAT format, if required */
      if (csa->out_cnf != NULL)
      {  ret = glp_write_cnfsat(csa->prob, csa->out_cnf);
         if (ret != 0)
         {  xprintf(
               "Unable to write problem in DIMACS CNF-SAT format\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
#endif
      /*--------------------------------------------------------------*/
      /* if only problem data check is required, skip computations */
      if (csa->check)
      {
#if 1 /* 29/III-2016 */
         /* report problem characteristics */
         int j, cnt = 0;
         xprintf("--- Problem Characteristics ---\n");
         xprintf("Number of rows               = %8d\n",
            glp_get_num_rows(csa->prob));
         xprintf("Number of columns            = %8d\n",
            glp_get_num_cols(csa->prob));
         xprintf("Number of non-zeros (matrix) = %8d\n",
            glp_get_num_nz(csa->prob));
         for (j = glp_get_num_cols(csa->prob); j >= 1; j--)
         {  if (glp_get_obj_coef(csa->prob, j) != 0.0)
               cnt++;
         }
         xprintf("Number of non-zeros (objrow) = %8d\n",
            cnt);
#endif
         ret = EXIT_SUCCESS;
         goto done;
      }
      /*--------------------------------------------------------------*/
      /* determine the solution type */
      if (!csa->nomip &&
          glp_get_num_int(csa->prob) + glp_get_num_bin(csa->prob) > 0)
      {  if (csa->solution == SOL_INTERIOR)
         {  xprintf("Interior-point method is not able to solve MIP pro"
               "blem; use --simplex\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         csa->solution = SOL_INTEGER;
      }
      /*--------------------------------------------------------------*/
      /* if solution is provided, read it and skip computations */
      if (csa->in_res != NULL)
      {  if (csa->solution == SOL_BASIC)
            ret = glp_read_sol(csa->prob, csa->in_res);
         else if (csa->solution == SOL_INTERIOR)
            ret = glp_read_ipt(csa->prob, csa->in_res);
         else if (csa->solution == SOL_INTEGER)
            ret = glp_read_mip(csa->prob, csa->in_res);
         else
            xassert(csa != csa);
         if (ret != 0)
         {  xprintf("Unable to read problem solution\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         goto skip;
      }
#if 1 /* 11/VII-2013 */
      /*--------------------------------------------------------------*/
      /* if initial MIP solution is provided, read it */
      if (csa->solution == SOL_INTEGER && csa->use_sol != NULL)
      {  ret = glp_read_mip(csa->prob, csa->use_sol);
         if (ret != 0)
         {  xprintf("Unable to read initial MIP solution\n");
            ret = EXIT_FAILURE;
            goto done;
         }
         csa->iocp.use_sol = GLP_ON;
      }
#endif
      /*--------------------------------------------------------------*/
      /* scale the problem data, if required */
      if (csa->scale)
      {  if (csa->solution == SOL_BASIC && !csa->smcp.presolve ||
             csa->solution == SOL_INTERIOR ||
             csa->solution == SOL_INTEGER && !csa->iocp.presolve)
            glp_scale_prob(csa->prob, GLP_SF_AUTO);
      }
      /*--------------------------------------------------------------*/
      /* construct starting LP basis */
      if (csa->solution == SOL_BASIC && !csa->smcp.presolve ||
          csa->solution == SOL_INTEGER && !csa->iocp.presolve)
      {  if (csa->crash == USE_STD_BASIS)
            glp_std_basis(csa->prob);
         else if (csa->crash == USE_ADV_BASIS)
            glp_adv_basis(csa->prob, 0);
         else if (csa->crash == USE_CPX_BASIS)
            glp_cpx_basis(csa->prob);
         else if (csa->crash == USE_INI_BASIS)
         {  ret = glp_read_sol(csa->prob, csa->ini_file);
            if (ret != 0)
            {  xprintf("Unable to read initial basis\n");
               ret = EXIT_FAILURE;
               goto done;
            }
         }
         else
            xassert(csa != csa);
      }
      /*--------------------------------------------------------------*/
      /* solve the problem */
      start = glp_time();
      if (csa->solution == SOL_BASIC)
      {  if (!csa->exact)
         {  glp_set_bfcp(csa->prob, &csa->bfcp);
            glp_simplex(csa->prob, &csa->smcp);
            if (csa->xcheck)
            {  if (csa->smcp.presolve &&
                   glp_get_status(csa->prob) != GLP_OPT)
                  xprintf("If you need to check final basis for non-opt"
                     "imal solution, use --nopresol\n");
               else
                  glp_exact(csa->prob, &csa->smcp);
            }
            if (csa->out_sol != NULL || csa->out_res != NULL)
            {  if (csa->smcp.presolve &&
                   glp_get_status(csa->prob) != GLP_OPT)
               xprintf("If you need actual output for non-optimal solut"
                  "ion, use --nopresol\n");
            }
         }
         else
            glp_exact(csa->prob, &csa->smcp);
      }
      else if (csa->solution == SOL_INTERIOR)
         glp_interior(csa->prob, &csa->iptcp);
#if 1 /* 15/VIII-2011 */
      else if (csa->solution == SOL_INTEGER && csa->minisat)
      {  if (glp_check_cnfsat(csa->prob) == 0)
            glp_minisat1(csa->prob);
         else
            glp_intfeas1(csa->prob, csa->use_bnd, csa->obj_bnd);
      }
#endif
      else if (csa->solution == SOL_INTEGER)
      {  glp_set_bfcp(csa->prob, &csa->bfcp);
         if (!csa->iocp.presolve)
            glp_simplex(csa->prob, &csa->smcp);
#if 0
         csa->iocp.msg_lev = GLP_MSG_DBG;
         csa->iocp.pp_tech = GLP_PP_NONE;
#endif
         glp_intopt(csa->prob, &csa->iocp);
      }
      else
         xassert(csa != csa);
      /*--------------------------------------------------------------*/
      /* display statistics */
      xprintf("Time used:   %.1f secs\n", glp_difftime(glp_time(),
         start));
#if 0 /* 16/II-2012 */
      {  glp_long tpeak;
         char buf[50];
         glp_mem_usage(NULL, NULL, NULL, &tpeak);
         xprintf("Memory used: %.1f Mb (%s bytes)\n",
            xltod(tpeak) / 1048576.0, xltoa(tpeak, buf));
      }
#else
      {  size_t tpeak;
         glp_mem_usage(NULL, NULL, NULL, &tpeak);
         xprintf("Memory used: %.1f Mb (%.0f bytes)\n",
            (double)tpeak / 1048576.0, (double)tpeak);
      }
#endif
      /*--------------------------------------------------------------*/
skip: /* postsolve the model, if necessary */
      if (csa->tran != NULL)
      {  if (csa->solution == SOL_BASIC)
         {  if (!(glp_get_status(csa->prob) == GLP_OPT ||
                  glp_get_status(csa->prob) == GLP_FEAS))
               ret = -1;
            else
               ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_SOL);
         }
         else if (csa->solution == SOL_INTERIOR)
         {  if (!(glp_ipt_status(csa->prob) == GLP_OPT ||
                  glp_ipt_status(csa->prob) == GLP_FEAS))
               ret = -1;
            else
               ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_IPT);
         }
         else if (csa->solution == SOL_INTEGER)
         {  if (!(glp_mip_status(csa->prob) == GLP_OPT ||
                  glp_mip_status(csa->prob) == GLP_FEAS))
               ret = -1;
            else
               ret = glp_mpl_postsolve(csa->tran, csa->prob, GLP_MIP);
         }
         else
            xassert(csa != csa);
         if (ret > 0)
         {  xprintf("Model postsolving error\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /*--------------------------------------------------------------*/
      /* write problem solution in printable format, if required */
      if (csa->out_sol != NULL)
      {  if (csa->solution == SOL_BASIC)
            ret = glp_print_sol(csa->prob, csa->out_sol);
         else if (csa->solution == SOL_INTERIOR)
            ret = glp_print_ipt(csa->prob, csa->out_sol);
         else if (csa->solution == SOL_INTEGER)
            ret = glp_print_mip(csa->prob, csa->out_sol);
         else
            xassert(csa != csa);
         if (ret != 0)
         {  xprintf("Unable to write problem solution\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write problem solution in printable format, if required */
      if (csa->out_res != NULL)
      {  if (csa->solution == SOL_BASIC)
            ret = glp_write_sol(csa->prob, csa->out_res);
         else if (csa->solution == SOL_INTERIOR)
            ret = glp_write_ipt(csa->prob, csa->out_res);
         else if (csa->solution == SOL_INTEGER)
            ret = glp_write_mip(csa->prob, csa->out_res);
         else
            xassert(csa != csa);
         if (ret != 0)
         {  xprintf("Unable to write problem solution\n");
            ret = EXIT_FAILURE;
            goto done;
         }
      }
      /* write sensitivity analysis report, if required */
      if (csa->out_ranges != NULL)
      {  if (csa->solution == SOL_BASIC)
         {  if (glp_get_status(csa->prob) == GLP_OPT)
            {  if (glp_bf_exists(csa->prob))
ranges:        {  ret = glp_print_ranges(csa->prob, 0, NULL, 0,
                     csa->out_ranges);
                  if (ret != 0)
                  {  xprintf("Unable to write sensitivity analysis repo"
                        "rt\n");
                     ret = EXIT_FAILURE;
                     goto done;
                  }
               }
               else
               {  ret = glp_factorize(csa->prob);
                  if (ret == 0) goto ranges;
                  xprintf("Cannot produce sensitivity analysis report d"
                     "ue to error in basis factorization (glp_factorize"
                     " returned %d); try --nopresol\n", ret);
               }
            }
            else
               xprintf("Cannot produce sensitivity analysis report for "
                  "non-optimal basic solution\n");
         }
         else
            xprintf("Cannot produce sensitivity analysis report for int"
               "erior-point or MIP solution\n");
      }
      /*--------------------------------------------------------------*/
      /* all seems to be ok */
      ret = EXIT_SUCCESS;
      /*--------------------------------------------------------------*/
done: /* delete the LP/MIP problem object */
      if (csa->prob != NULL)
         glp_delete_prob(csa->prob);
      /* free the translator workspace, if necessary */
      if (csa->tran != NULL)
         glp_mpl_free_wksp(csa->tran);
      /* delete the network problem object, if necessary */
      if (csa->graph != NULL)
         glp_delete_graph(csa->graph);
#if 0 /* 23/XI-2015 */
      xassert(gmp_pool_count() == 0);
      gmp_free_mem();
#endif
      /* close log file, if necessary */
      if (csa->log_file != NULL) glp_close_tee();
      /* check that no memory blocks are still allocated */
#if 0 /* 16/II-2012 */
      {  int count;
         glp_long total;
         glp_mem_usage(&count, NULL, &total, NULL);
         if (count != 0)
            xerror("Error: %d memory block(s) were lost\n", count);
         xassert(count == 0);
         xassert(total.lo == 0 && total.hi == 0);
      }
#else
      {  int count;
         size_t total;
         glp_mem_usage(&count, NULL, &total, NULL);
         if (count != 0)
            xerror("Error: %d memory block(s) were lost\n", count);
         xassert(total == 0);
      }
#endif
      /* free the GLPK environment */
      glp_free_env();
      /* return to the control program */
      return ret;
}

/* eof */
