src/glpcpx.c
changeset 2 4c8956a7bdf4
equal deleted inserted replaced
-1:000000000000 0:21bea247e59e
       
     1 /* glpcpx.c (CPLEX LP format routines) */
       
     2 
       
     3 /***********************************************************************
       
     4 *  This code is part of GLPK (GNU Linear Programming Kit).
       
     5 *
       
     6 *  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
       
     7 *  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
       
     8 *  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
       
     9 *  E-mail: <mao@gnu.org>.
       
    10 *
       
    11 *  GLPK is free software: you can redistribute it and/or modify it
       
    12 *  under the terms of the GNU General Public License as published by
       
    13 *  the Free Software Foundation, either version 3 of the License, or
       
    14 *  (at your option) any later version.
       
    15 *
       
    16 *  GLPK is distributed in the hope that it will be useful, but WITHOUT
       
    17 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
       
    18 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
       
    19 *  License for more details.
       
    20 *
       
    21 *  You should have received a copy of the GNU General Public License
       
    22 *  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
       
    23 ***********************************************************************/
       
    24 
       
    25 #include "glpapi.h"
       
    26 
       
    27 /***********************************************************************
       
    28 *  NAME
       
    29 *
       
    30 *  glp_init_cpxcp - initialize CPLEX LP format control parameters
       
    31 *
       
    32 *  SYNOPSIS
       
    33 *
       
    34 *  void glp_init_cpxcp(glp_cpxcp *parm):
       
    35 *
       
    36 *  The routine glp_init_cpxcp initializes control parameters used by
       
    37 *  the CPLEX LP input/output routines glp_read_lp and glp_write_lp with
       
    38 *  default values.
       
    39 *
       
    40 *  Default values of the control parameters are stored in the glp_cpxcp
       
    41 *  structure, which the parameter parm points to. */
       
    42 
       
    43 void glp_init_cpxcp(glp_cpxcp *parm)
       
    44 {     xassert(parm != NULL);
       
    45       return;
       
    46 }
       
    47 
       
    48 static void check_parm(const char *func, const glp_cpxcp *parm)
       
    49 {     /* check control parameters */
       
    50       xassert(func != NULL);
       
    51       xassert(parm != NULL);
       
    52       return;
       
    53 }
       
    54 
       
    55 /***********************************************************************
       
    56 *  NAME
       
    57 *
       
    58 *  glp_read_lp - read problem data in CPLEX LP format
       
    59 *
       
    60 *  SYNOPSIS
       
    61 *
       
    62 *  int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char
       
    63 *     *fname);
       
    64 *
       
    65 *  DESCRIPTION
       
    66 *
       
    67 *  The routine glp_read_lp reads problem data in CPLEX LP format from
       
    68 *  a text file.
       
    69 *
       
    70 *  The parameter parm is a pointer to the structure glp_cpxcp, which
       
    71 *  specifies control parameters used by the routine. If parm is NULL,
       
    72 *  the routine uses default settings.
       
    73 *
       
    74 *  The character string fname specifies a name of the text file to be
       
    75 *  read.
       
    76 *
       
    77 *  Note that before reading data the current content of the problem
       
    78 *  object is completely erased with the routine glp_erase_prob.
       
    79 *
       
    80 *  RETURNS
       
    81 *
       
    82 *  If the operation was successful, the routine glp_read_lp returns
       
    83 *  zero. Otherwise, it prints an error message and returns non-zero. */
       
    84 
       
    85 struct csa
       
    86 {     /* common storage area */
       
    87       glp_prob *P;
       
    88       /* LP/MIP problem object */
       
    89       const glp_cpxcp *parm;
       
    90       /* pointer to control parameters */
       
    91       const char *fname;
       
    92       /* name of input CPLEX LP file */
       
    93       XFILE *fp;
       
    94       /* stream assigned to input CPLEX LP file */
       
    95       jmp_buf jump;
       
    96       /* label for go to in case of error */
       
    97       int count;
       
    98       /* line count */
       
    99       int c;
       
   100       /* current character or XEOF */
       
   101       int token;
       
   102       /* current token: */
       
   103 #define T_EOF        0x00  /* end of file */
       
   104 #define T_MINIMIZE   0x01  /* keyword 'minimize' */
       
   105 #define T_MAXIMIZE   0x02  /* keyword 'maximize' */
       
   106 #define T_SUBJECT_TO 0x03  /* keyword 'subject to' */
       
   107 #define T_BOUNDS     0x04  /* keyword 'bounds' */
       
   108 #define T_GENERAL    0x05  /* keyword 'general' */
       
   109 #define T_INTEGER    0x06  /* keyword 'integer' */
       
   110 #define T_BINARY     0x07  /* keyword 'binary' */
       
   111 #define T_END        0x08  /* keyword 'end' */
       
   112 #define T_NAME       0x09  /* symbolic name */
       
   113 #define T_NUMBER     0x0A  /* numeric constant */
       
   114 #define T_PLUS       0x0B  /* delimiter '+' */
       
   115 #define T_MINUS      0x0C  /* delimiter '-' */
       
   116 #define T_COLON      0x0D  /* delimiter ':' */
       
   117 #define T_LE         0x0E  /* delimiter '<=' */
       
   118 #define T_GE         0x0F  /* delimiter '>=' */
       
   119 #define T_EQ         0x10  /* delimiter '=' */
       
   120       char image[255+1];
       
   121       /* image of current token */
       
   122       int imlen;
       
   123       /* length of token image */
       
   124       double value;
       
   125       /* value of numeric constant */
       
   126       int n_max;
       
   127       /* length of the following five arrays (enlarged automatically,
       
   128          if necessary) */
       
   129       int *ind; /* int ind[1+n_max]; */
       
   130       double *val; /* double val[1+n_max]; */
       
   131       char *flag; /* char flag[1+n_max]; */
       
   132       /* working arrays used to construct linear forms */
       
   133       double *lb; /* double lb[1+n_max]; */
       
   134       double *ub; /* double ub[1+n_max]; */
       
   135       /* lower and upper bounds of variables (columns) */
       
   136 };
       
   137 
       
   138 #define CHAR_SET "!\"#$%&()/,.;?@_`'{}|~"
       
   139 /* characters, which may appear in symbolic names */
       
   140 
       
   141 static void error(struct csa *csa, const char *fmt, ...)
       
   142 {     /* print error message and terminate processing */
       
   143       va_list arg;
       
   144       xprintf("%s:%d: ", csa->fname, csa->count);
       
   145       va_start(arg, fmt);
       
   146       xvprintf(fmt, arg);
       
   147       va_end(arg);
       
   148       longjmp(csa->jump, 1);
       
   149       /* no return */
       
   150 }
       
   151 
       
   152 static void warning(struct csa *csa, const char *fmt, ...)
       
   153 {     /* print warning message and continue processing */
       
   154       va_list arg;
       
   155       xprintf("%s:%d: warning: ", csa->fname, csa->count);
       
   156       va_start(arg, fmt);
       
   157       xvprintf(fmt, arg);
       
   158       va_end(arg);
       
   159       return;
       
   160 }
       
   161 
       
   162 static void read_char(struct csa *csa)
       
   163 {     /* read next character from input file */
       
   164       int c;
       
   165       xassert(csa->c != XEOF);
       
   166       if (csa->c == '\n') csa->count++;
       
   167       c = xfgetc(csa->fp);
       
   168       if (c < 0)
       
   169       {  if (xferror(csa->fp))
       
   170             error(csa, "read error - %s\n", xerrmsg());
       
   171          else if (csa->c == '\n')
       
   172          {  csa->count--;
       
   173             c = XEOF;
       
   174          }
       
   175          else
       
   176          {  warning(csa, "missing final end of line\n");
       
   177             c = '\n';
       
   178          }
       
   179       }
       
   180       else if (c == '\n')
       
   181          ;
       
   182       else if (isspace(c))
       
   183          c = ' ';
       
   184       else if (iscntrl(c))
       
   185          error(csa, "invalid control character 0x%02X\n", c);
       
   186       csa->c = c;
       
   187       return;
       
   188 }
       
   189 
       
   190 static void add_char(struct csa *csa)
       
   191 {     /* append current character to current token */
       
   192       if (csa->imlen == sizeof(csa->image)-1)
       
   193          error(csa, "token `%.15s...' too long\n", csa->image);
       
   194       csa->image[csa->imlen++] = (char)csa->c;
       
   195       csa->image[csa->imlen] = '\0';
       
   196       read_char(csa);
       
   197       return;
       
   198 }
       
   199 
       
   200 static int the_same(char *s1, char *s2)
       
   201 {     /* compare two character strings ignoring case sensitivity */
       
   202       for (; *s1 != '\0'; s1++, s2++)
       
   203       {  if (tolower((unsigned char)*s1) != tolower((unsigned char)*s2))
       
   204             return 0;
       
   205       }
       
   206       return 1;
       
   207 }
       
   208 
       
   209 static void scan_token(struct csa *csa)
       
   210 {     /* scan next token */
       
   211       int flag;
       
   212       csa->token = -1;
       
   213       csa->image[0] = '\0';
       
   214       csa->imlen = 0;
       
   215       csa->value = 0.0;
       
   216 loop: flag = 0;
       
   217       /* skip non-significant characters */
       
   218       while (csa->c == ' ') read_char(csa);
       
   219       /* recognize and scan current token */
       
   220       if (csa->c == XEOF)
       
   221          csa->token = T_EOF;
       
   222       else if (csa->c == '\n')
       
   223       {  read_char(csa);
       
   224          /* if the next character is letter, it may begin a keyword */
       
   225          if (isalpha(csa->c))
       
   226          {  flag = 1;
       
   227             goto name;
       
   228          }
       
   229          goto loop;
       
   230       }
       
   231       else if (csa->c == '\\')
       
   232       {  /* comment; ignore everything until end-of-line */
       
   233          while (csa->c != '\n') read_char(csa);
       
   234          goto loop;
       
   235       }
       
   236       else if (isalpha(csa->c) || csa->c != '.' && strchr(CHAR_SET,
       
   237          csa->c) != NULL)
       
   238 name: {  /* symbolic name */
       
   239          csa->token = T_NAME;
       
   240          while (isalnum(csa->c) || strchr(CHAR_SET, csa->c) != NULL)
       
   241             add_char(csa);
       
   242          if (flag)
       
   243          {  /* check for keyword */
       
   244             if (the_same(csa->image, "minimize"))
       
   245                csa->token = T_MINIMIZE;
       
   246             else if (the_same(csa->image, "minimum"))
       
   247                csa->token = T_MINIMIZE;
       
   248             else if (the_same(csa->image, "min"))
       
   249                csa->token = T_MINIMIZE;
       
   250             else if (the_same(csa->image, "maximize"))
       
   251                csa->token = T_MAXIMIZE;
       
   252             else if (the_same(csa->image, "maximum"))
       
   253                csa->token = T_MAXIMIZE;
       
   254             else if (the_same(csa->image, "max"))
       
   255                csa->token = T_MAXIMIZE;
       
   256             else if (the_same(csa->image, "subject"))
       
   257             {  if (csa->c == ' ')
       
   258                {  read_char(csa);
       
   259                   if (tolower(csa->c) == 't')
       
   260                   {  csa->token = T_SUBJECT_TO;
       
   261                      csa->image[csa->imlen++] = ' ';
       
   262                      csa->image[csa->imlen] = '\0';
       
   263                      add_char(csa);
       
   264                      if (tolower(csa->c) != 'o')
       
   265                         error(csa, "keyword `subject to' incomplete\n");
       
   266                      add_char(csa);
       
   267                      if (isalpha(csa->c))
       
   268                         error(csa, "keyword `%s%c...' not recognized\n",
       
   269                            csa->image, csa->c);
       
   270                   }
       
   271                }
       
   272             }
       
   273             else if (the_same(csa->image, "such"))
       
   274             {  if (csa->c == ' ')
       
   275                {  read_char(csa);
       
   276                   if (tolower(csa->c) == 't')
       
   277                   {  csa->token = T_SUBJECT_TO;
       
   278                      csa->image[csa->imlen++] = ' ';
       
   279                      csa->image[csa->imlen] = '\0';
       
   280                      add_char(csa);
       
   281                      if (tolower(csa->c) != 'h')
       
   282 err:                    error(csa, "keyword `such that' incomplete\n");
       
   283                      add_char(csa);
       
   284                      if (tolower(csa->c) != 'a') goto err;
       
   285                      add_char(csa);
       
   286                      if (tolower(csa->c) != 't') goto err;
       
   287                      add_char(csa);
       
   288                      if (isalpha(csa->c))
       
   289                         error(csa, "keyword `%s%c...' not recognized\n",
       
   290                            csa->image, csa->c);
       
   291                   }
       
   292                }
       
   293             }
       
   294             else if (the_same(csa->image, "st"))
       
   295                csa->token = T_SUBJECT_TO;
       
   296             else if (the_same(csa->image, "s.t."))
       
   297                csa->token = T_SUBJECT_TO;
       
   298             else if (the_same(csa->image, "st."))
       
   299                csa->token = T_SUBJECT_TO;
       
   300             else if (the_same(csa->image, "bounds"))
       
   301                csa->token = T_BOUNDS;
       
   302             else if (the_same(csa->image, "bound"))
       
   303                csa->token = T_BOUNDS;
       
   304             else if (the_same(csa->image, "general"))
       
   305                csa->token = T_GENERAL;
       
   306             else if (the_same(csa->image, "generals"))
       
   307                csa->token = T_GENERAL;
       
   308             else if (the_same(csa->image, "gen"))
       
   309                csa->token = T_GENERAL;
       
   310             else if (the_same(csa->image, "integer"))
       
   311                csa->token = T_INTEGER;
       
   312             else if (the_same(csa->image, "integers"))
       
   313                csa->token = T_INTEGER;
       
   314             else if (the_same(csa->image, "int"))
       
   315               csa->token = T_INTEGER;
       
   316             else if (the_same(csa->image, "binary"))
       
   317                csa->token = T_BINARY;
       
   318             else if (the_same(csa->image, "binaries"))
       
   319                csa->token = T_BINARY;
       
   320             else if (the_same(csa->image, "bin"))
       
   321                csa->token = T_BINARY;
       
   322             else if (the_same(csa->image, "end"))
       
   323                csa->token = T_END;
       
   324          }
       
   325       }
       
   326       else if (isdigit(csa->c) || csa->c == '.')
       
   327       {  /* numeric constant */
       
   328          csa->token = T_NUMBER;
       
   329          /* scan integer part */
       
   330          while (isdigit(csa->c)) add_char(csa);
       
   331          /* scan optional fractional part (it is mandatory, if there is
       
   332             no integer part) */
       
   333          if (csa->c == '.')
       
   334          {  add_char(csa);
       
   335             if (csa->imlen == 1 && !isdigit(csa->c))
       
   336                error(csa, "invalid use of decimal point\n");
       
   337             while (isdigit(csa->c)) add_char(csa);
       
   338          }
       
   339          /* scan optional decimal exponent */
       
   340          if (csa->c == 'e' || csa->c == 'E')
       
   341          {  add_char(csa);
       
   342             if (csa->c == '+' || csa->c == '-') add_char(csa);
       
   343             if (!isdigit(csa->c))
       
   344                error(csa, "numeric constant `%s' incomplete\n",
       
   345                   csa->image);
       
   346             while (isdigit(csa->c)) add_char(csa);
       
   347          }
       
   348          /* convert the numeric constant to floating-point */
       
   349          if (str2num(csa->image, &csa->value))
       
   350             error(csa, "numeric constant `%s' out of range\n",
       
   351                csa->image);
       
   352       }
       
   353       else if (csa->c == '+')
       
   354          csa->token = T_PLUS, add_char(csa);
       
   355       else if (csa->c == '-')
       
   356          csa->token = T_MINUS, add_char(csa);
       
   357       else if (csa->c == ':')
       
   358          csa->token = T_COLON, add_char(csa);
       
   359       else if (csa->c == '<')
       
   360       {  csa->token = T_LE, add_char(csa);
       
   361          if (csa->c == '=') add_char(csa);
       
   362       }
       
   363       else if (csa->c == '>')
       
   364       {  csa->token = T_GE, add_char(csa);
       
   365          if (csa->c == '=') add_char(csa);
       
   366       }
       
   367       else if (csa->c == '=')
       
   368       {  csa->token = T_EQ, add_char(csa);
       
   369          if (csa->c == '<')
       
   370             csa->token = T_LE, add_char(csa);
       
   371          else if (csa->c == '>')
       
   372             csa->token = T_GE, add_char(csa);
       
   373       }
       
   374       else
       
   375          error(csa, "character `%c' not recognized\n", csa->c);
       
   376       /* skip non-significant characters */
       
   377       while (csa->c == ' ') read_char(csa);
       
   378       return;
       
   379 }
       
   380 
       
   381 static int find_col(struct csa *csa, char *name)
       
   382 {     /* find column by its symbolic name */
       
   383       int j;
       
   384       j = glp_find_col(csa->P, name);
       
   385       if (j == 0)
       
   386       {  /* not found; create new column */
       
   387          j = glp_add_cols(csa->P, 1);
       
   388          glp_set_col_name(csa->P, j, name);
       
   389          /* enlarge working arrays, if necessary */
       
   390          if (csa->n_max < j)
       
   391          {  int n_max = csa->n_max;
       
   392             int *ind = csa->ind;
       
   393             double *val = csa->val;
       
   394             char *flag = csa->flag;
       
   395             double *lb = csa->lb;
       
   396             double *ub = csa->ub;
       
   397             csa->n_max += csa->n_max;
       
   398             csa->ind = xcalloc(1+csa->n_max, sizeof(int));
       
   399             memcpy(&csa->ind[1], &ind[1], n_max * sizeof(int));
       
   400             xfree(ind);
       
   401             csa->val = xcalloc(1+csa->n_max, sizeof(double));
       
   402             memcpy(&csa->val[1], &val[1], n_max * sizeof(double));
       
   403             xfree(val);
       
   404             csa->flag = xcalloc(1+csa->n_max, sizeof(char));
       
   405             memset(&csa->flag[1], 0, csa->n_max * sizeof(char));
       
   406             memcpy(&csa->flag[1], &flag[1], n_max * sizeof(char));
       
   407             xfree(flag);
       
   408             csa->lb = xcalloc(1+csa->n_max, sizeof(double));
       
   409             memcpy(&csa->lb[1], &lb[1], n_max * sizeof(double));
       
   410             xfree(lb);
       
   411             csa->ub = xcalloc(1+csa->n_max, sizeof(double));
       
   412             memcpy(&csa->ub[1], &ub[1], n_max * sizeof(double));
       
   413             xfree(ub);
       
   414          }
       
   415          csa->lb[j] = +DBL_MAX, csa->ub[j] = -DBL_MAX;
       
   416       }
       
   417       return j;
       
   418 }
       
   419 
       
   420 /***********************************************************************
       
   421 *  parse_linear_form - parse linear form
       
   422 *
       
   423 *  This routine parses the linear form using the following syntax:
       
   424 *
       
   425 *  <variable> ::= <symbolic name>
       
   426 *  <coefficient> ::= <numeric constant>
       
   427 *  <term> ::= <variable> | <numeric constant> <variable>
       
   428 *  <linear form> ::= <term> | + <term> | - <term> |
       
   429 *     <linear form> + <term> | <linear form> - <term>
       
   430 *
       
   431 *  The routine returns the number of terms in the linear form. */
       
   432 
       
   433 static int parse_linear_form(struct csa *csa)
       
   434 {     int j, k, len = 0, newlen;
       
   435       double s, coef;
       
   436 loop: /* parse an optional sign */
       
   437       if (csa->token == T_PLUS)
       
   438          s = +1.0, scan_token(csa);
       
   439       else if (csa->token == T_MINUS)
       
   440          s = -1.0, scan_token(csa);
       
   441       else
       
   442          s = +1.0;
       
   443       /* parse an optional coefficient */
       
   444       if (csa->token == T_NUMBER)
       
   445          coef = csa->value, scan_token(csa);
       
   446       else
       
   447          coef = 1.0;
       
   448       /* parse a variable name */
       
   449       if (csa->token != T_NAME)
       
   450          error(csa, "missing variable name\n");
       
   451       /* find the corresponding column */
       
   452       j = find_col(csa, csa->image);
       
   453       /* check if the variable is already used in the linear form */
       
   454       if (csa->flag[j])
       
   455          error(csa, "multiple use of variable `%s' not allowed\n",
       
   456             csa->image);
       
   457       /* add new term to the linear form */
       
   458       len++, csa->ind[len] = j, csa->val[len] = s * coef;
       
   459       /* and mark that the variable is used in the linear form */
       
   460       csa->flag[j] = 1;
       
   461       scan_token(csa);
       
   462       /* if the next token is a sign, there is another term */
       
   463       if (csa->token == T_PLUS || csa->token == T_MINUS) goto loop;
       
   464       /* clear marks of the variables used in the linear form */
       
   465       for (k = 1; k <= len; k++) csa->flag[csa->ind[k]] = 0;
       
   466       /* remove zero coefficients */
       
   467       newlen = 0;
       
   468       for (k = 1; k <= len; k++)
       
   469       {  if (csa->val[k] != 0.0)
       
   470          {  newlen++;
       
   471             csa->ind[newlen] = csa->ind[k];
       
   472             csa->val[newlen] = csa->val[k];
       
   473          }
       
   474       }
       
   475       return newlen;
       
   476 }
       
   477 
       
   478 /***********************************************************************
       
   479 *  parse_objective - parse objective function
       
   480 *
       
   481 *  This routine parses definition of the objective function using the
       
   482 *  following syntax:
       
   483 *
       
   484 *  <obj sense> ::= minimize | minimum | min | maximize | maximum | max
       
   485 *  <obj name> ::= <empty> | <symbolic name> :
       
   486 *  <obj function> ::= <obj sense> <obj name> <linear form> */
       
   487 
       
   488 static void parse_objective(struct csa *csa)
       
   489 {     /* parse objective sense */
       
   490       int k, len;
       
   491       /* parse the keyword 'minimize' or 'maximize' */
       
   492       if (csa->token == T_MINIMIZE)
       
   493          glp_set_obj_dir(csa->P, GLP_MIN);
       
   494       else if (csa->token == T_MAXIMIZE)
       
   495          glp_set_obj_dir(csa->P, GLP_MAX);
       
   496       else
       
   497          xassert(csa != csa);
       
   498       scan_token(csa);
       
   499       /* parse objective name */
       
   500       if (csa->token == T_NAME && csa->c == ':')
       
   501       {  /* objective name is followed by a colon */
       
   502          glp_set_obj_name(csa->P, csa->image);
       
   503          scan_token(csa);
       
   504          xassert(csa->token == T_COLON);
       
   505          scan_token(csa);
       
   506       }
       
   507       else
       
   508       {  /* objective name is not specified; use default */
       
   509          glp_set_obj_name(csa->P, "obj");
       
   510       }
       
   511       /* parse linear form */
       
   512       len = parse_linear_form(csa);
       
   513       for (k = 1; k <= len; k++)
       
   514          glp_set_obj_coef(csa->P, csa->ind[k], csa->val[k]);
       
   515       return;
       
   516 }
       
   517 
       
   518 /***********************************************************************
       
   519 *  parse_constraints - parse constraints section
       
   520 *
       
   521 *  This routine parses the constraints section using the following
       
   522 *  syntax:
       
   523 *
       
   524 *  <row name> ::= <empty> | <symbolic name> :
       
   525 *  <row sense> ::= < | <= | =< | > | >= | => | =
       
   526 *  <right-hand side> ::= <numeric constant> | + <numeric constant> |
       
   527 *     - <numeric constant>
       
   528 *  <constraint> ::= <row name> <linear form> <row sense>
       
   529 *     <right-hand side>
       
   530 *  <subject to> ::= subject to | such that | st | s.t. | st.
       
   531 *  <constraints section> ::= <subject to> <constraint> |
       
   532 *     <constraints section> <constraint> */
       
   533 
       
   534 static void parse_constraints(struct csa *csa)
       
   535 {     int i, len, type;
       
   536       double s;
       
   537       /* parse the keyword 'subject to' */
       
   538       xassert(csa->token == T_SUBJECT_TO);
       
   539       scan_token(csa);
       
   540 loop: /* create new row (constraint) */
       
   541       i = glp_add_rows(csa->P, 1);
       
   542       /* parse row name */
       
   543       if (csa->token == T_NAME && csa->c == ':')
       
   544       {  /* row name is followed by a colon */
       
   545          if (glp_find_row(csa->P, csa->image) != 0)
       
   546             error(csa, "constraint `%s' multiply defined\n",
       
   547                csa->image);
       
   548          glp_set_row_name(csa->P, i, csa->image);
       
   549          scan_token(csa);
       
   550          xassert(csa->token == T_COLON);
       
   551          scan_token(csa);
       
   552       }
       
   553       else
       
   554       {  /* row name is not specified; use default */
       
   555          char name[50];
       
   556          sprintf(name, "r.%d", csa->count);
       
   557          glp_set_row_name(csa->P, i, name);
       
   558       }
       
   559       /* parse linear form */
       
   560       len = parse_linear_form(csa);
       
   561       glp_set_mat_row(csa->P, i, len, csa->ind, csa->val);
       
   562       /* parse constraint sense */
       
   563       if (csa->token == T_LE)
       
   564          type = GLP_UP, scan_token(csa);
       
   565       else if (csa->token == T_GE)
       
   566          type = GLP_LO, scan_token(csa);
       
   567       else if (csa->token == T_EQ)
       
   568          type = GLP_FX, scan_token(csa);
       
   569       else
       
   570          error(csa, "missing constraint sense\n");
       
   571       /* parse right-hand side */
       
   572       if (csa->token == T_PLUS)
       
   573          s = +1.0, scan_token(csa);
       
   574       else if (csa->token == T_MINUS)
       
   575          s = -1.0, scan_token(csa);
       
   576       else
       
   577          s = +1.0;
       
   578       if (csa->token != T_NUMBER)
       
   579          error(csa, "missing right-hand side\n");
       
   580       glp_set_row_bnds(csa->P, i, type, s * csa->value, s * csa->value);
       
   581       /* the rest of the current line must be empty */
       
   582       if (!(csa->c == '\n' || csa->c == XEOF))
       
   583          error(csa, "invalid symbol(s) beyond right-hand side\n");
       
   584       scan_token(csa);
       
   585       /* if the next token is a sign, numeric constant, or a symbolic
       
   586          name, here is another constraint */
       
   587       if (csa->token == T_PLUS || csa->token == T_MINUS ||
       
   588           csa->token == T_NUMBER || csa->token == T_NAME) goto loop;
       
   589       return;
       
   590 }
       
   591 
       
   592 static void set_lower_bound(struct csa *csa, int j, double lb)
       
   593 {     /* set lower bound of j-th variable */
       
   594       if (csa->lb[j] != +DBL_MAX)
       
   595       {  warning(csa, "lower bound of variable `%s' redefined\n",
       
   596             glp_get_col_name(csa->P, j));
       
   597       }
       
   598       csa->lb[j] = lb;
       
   599       return;
       
   600 }
       
   601 
       
   602 static void set_upper_bound(struct csa *csa, int j, double ub)
       
   603 {     /* set upper bound of j-th variable */
       
   604       if (csa->ub[j] != -DBL_MAX)
       
   605       {  warning(csa, "upper bound of variable `%s' redefined\n",
       
   606             glp_get_col_name(csa->P, j));
       
   607       }
       
   608       csa->ub[j] = ub;
       
   609       return;
       
   610 }
       
   611 
       
   612 /***********************************************************************
       
   613 *  parse_bounds - parse bounds section
       
   614 *
       
   615 *  This routine parses the bounds section using the following syntax:
       
   616 *
       
   617 *  <variable> ::= <symbolic name>
       
   618 *  <infinity> ::= infinity | inf
       
   619 *  <bound> ::= <numeric constant> | + <numeric constant> |
       
   620 *     - <numeric constant> | + <infinity> | - <infinity>
       
   621 *  <lt> ::= < | <= | =<
       
   622 *  <gt> ::= > | >= | =>
       
   623 *  <bound definition> ::= <bound> <lt> <variable> <lt> <bound> |
       
   624 *     <bound> <lt> <variable> | <variable> <lt> <bound> |
       
   625 *     <variable> <gt> <bound> | <variable> = <bound> | <variable> free
       
   626 *  <bounds> ::= bounds | bound
       
   627 *  <bounds section> ::= <bounds> |
       
   628 *     <bounds section> <bound definition> */
       
   629 
       
   630 static void parse_bounds(struct csa *csa)
       
   631 {     int j, lb_flag;
       
   632       double lb, s;
       
   633       /* parse the keyword 'bounds' */
       
   634       xassert(csa->token == T_BOUNDS);
       
   635       scan_token(csa);
       
   636 loop: /* bound definition can start with a sign, numeric constant, or
       
   637          a symbolic name */
       
   638       if (!(csa->token == T_PLUS || csa->token == T_MINUS ||
       
   639             csa->token == T_NUMBER || csa->token == T_NAME)) goto done;
       
   640       /* parse bound definition */
       
   641       if (csa->token == T_PLUS || csa->token == T_MINUS)
       
   642       {  /* parse signed lower bound */
       
   643          lb_flag = 1;
       
   644          s = (csa->token == T_PLUS ? +1.0 : -1.0);
       
   645          scan_token(csa);
       
   646          if (csa->token == T_NUMBER)
       
   647             lb = s * csa->value, scan_token(csa);
       
   648          else if (the_same(csa->image, "infinity") ||
       
   649                   the_same(csa->image, "inf"))
       
   650          {  if (s > 0.0)
       
   651                error(csa, "invalid use of `+inf' as lower bound\n");
       
   652             lb = -DBL_MAX, scan_token(csa);
       
   653          }
       
   654          else
       
   655             error(csa, "missing lower bound\n");
       
   656       }
       
   657       else if (csa->token == T_NUMBER)
       
   658       {  /* parse unsigned lower bound */
       
   659          lb_flag = 1;
       
   660          lb = csa->value, scan_token(csa);
       
   661       }
       
   662       else
       
   663       {  /* lower bound is not specified */
       
   664          lb_flag = 0;
       
   665       }
       
   666       /* parse the token that should follow the lower bound */
       
   667       if (lb_flag)
       
   668       {  if (csa->token != T_LE)
       
   669             error(csa, "missing `<', `<=', or `=<' after lower bound\n")
       
   670                ;
       
   671          scan_token(csa);
       
   672       }
       
   673       /* parse variable name */
       
   674       if (csa->token != T_NAME)
       
   675          error(csa, "missing variable name\n");
       
   676       j = find_col(csa, csa->image);
       
   677       /* set lower bound */
       
   678       if (lb_flag) set_lower_bound(csa, j, lb);
       
   679       scan_token(csa);
       
   680       /* parse the context that follows the variable name */
       
   681       if (csa->token == T_LE)
       
   682       {  /* parse upper bound */
       
   683          scan_token(csa);
       
   684          if (csa->token == T_PLUS || csa->token == T_MINUS)
       
   685          {  /* parse signed upper bound */
       
   686             s = (csa->token == T_PLUS ? +1.0 : -1.0);
       
   687             scan_token(csa);
       
   688             if (csa->token == T_NUMBER)
       
   689             {  set_upper_bound(csa, j, s * csa->value);
       
   690                scan_token(csa);
       
   691             }
       
   692             else if (the_same(csa->image, "infinity") ||
       
   693                      the_same(csa->image, "inf"))
       
   694             {  if (s < 0.0)
       
   695                   error(csa, "invalid use of `-inf' as upper bound\n");
       
   696                set_upper_bound(csa, j, +DBL_MAX);
       
   697                scan_token(csa);
       
   698             }
       
   699             else
       
   700                error(csa, "missing upper bound\n");
       
   701          }
       
   702          else if (csa->token == T_NUMBER)
       
   703          {  /* parse unsigned upper bound */
       
   704             set_upper_bound(csa, j, csa->value);
       
   705             scan_token(csa);
       
   706          }
       
   707          else
       
   708             error(csa, "missing upper bound\n");
       
   709       }
       
   710       else if (csa->token == T_GE)
       
   711       {  /* parse lower bound */
       
   712          if (lb_flag)
       
   713          {  /* the context '... <= x >= ...' is invalid */
       
   714             error(csa, "invalid bound definition\n");
       
   715          }
       
   716          scan_token(csa);
       
   717          if (csa->token == T_PLUS || csa->token == T_MINUS)
       
   718          {  /* parse signed lower bound */
       
   719             s = (csa->token == T_PLUS ? +1.0 : -1.0);
       
   720             scan_token(csa);
       
   721             if (csa->token == T_NUMBER)
       
   722             {  set_lower_bound(csa, j, s * csa->value);
       
   723                scan_token(csa);
       
   724             }
       
   725             else if (the_same(csa->image, "infinity") ||
       
   726                      the_same(csa->image, "inf") == 0)
       
   727             {  if (s > 0.0)
       
   728                   error(csa, "invalid use of `+inf' as lower bound\n");
       
   729                set_lower_bound(csa, j, -DBL_MAX);
       
   730                scan_token(csa);
       
   731             }
       
   732             else
       
   733                error(csa, "missing lower bound\n");
       
   734          }
       
   735          else if (csa->token == T_NUMBER)
       
   736          {  /* parse unsigned lower bound */
       
   737             set_lower_bound(csa, j, csa->value);
       
   738             scan_token(csa);
       
   739          }
       
   740          else
       
   741             error(csa, "missing lower bound\n");
       
   742       }
       
   743       else if (csa->token == T_EQ)
       
   744       {  /* parse fixed value */
       
   745          if (lb_flag)
       
   746          {  /* the context '... <= x = ...' is invalid */
       
   747             error(csa, "invalid bound definition\n");
       
   748          }
       
   749          scan_token(csa);
       
   750          if (csa->token == T_PLUS || csa->token == T_MINUS)
       
   751          {  /* parse signed fixed value */
       
   752             s = (csa->token == T_PLUS ? +1.0 : -1.0);
       
   753             scan_token(csa);
       
   754             if (csa->token == T_NUMBER)
       
   755             {  set_lower_bound(csa, j, s * csa->value);
       
   756                set_upper_bound(csa, j, s * csa->value);
       
   757                scan_token(csa);
       
   758             }
       
   759             else
       
   760                error(csa, "missing fixed value\n");
       
   761          }
       
   762          else if (csa->token == T_NUMBER)
       
   763          {  /* parse unsigned fixed value */
       
   764             set_lower_bound(csa, j, csa->value);
       
   765             set_upper_bound(csa, j, csa->value);
       
   766             scan_token(csa);
       
   767          }
       
   768          else
       
   769             error(csa, "missing fixed value\n");
       
   770       }
       
   771       else if (the_same(csa->image, "free"))
       
   772       {  /* parse the keyword 'free' */
       
   773          if (lb_flag)
       
   774          {  /* the context '... <= x free ...' is invalid */
       
   775             error(csa, "invalid bound definition\n");
       
   776          }
       
   777          set_lower_bound(csa, j, -DBL_MAX);
       
   778          set_upper_bound(csa, j, +DBL_MAX);
       
   779          scan_token(csa);
       
   780       }
       
   781       else if (!lb_flag)
       
   782       {  /* neither lower nor upper bounds are specified */
       
   783          error(csa, "invalid bound definition\n");
       
   784       }
       
   785       goto loop;
       
   786 done: return;
       
   787 }
       
   788 
       
   789 /***********************************************************************
       
   790 *  parse_integer - parse general, integer, or binary section
       
   791 *
       
   792 *  <variable> ::= <symbolic name>
       
   793 *  <general> ::= general | generals | gen
       
   794 *  <integer> ::= integer | integers | int
       
   795 *  <binary> ::= binary | binaries | bin
       
   796 *  <section head> ::= <general> <integer> <binary>
       
   797 *  <additional section> ::= <section head> |
       
   798 *     <additional section> <variable> */
       
   799 
       
   800 static void parse_integer(struct csa *csa)
       
   801 {     int j, binary;
       
   802       /* parse the keyword 'general', 'integer', or 'binary' */
       
   803       if (csa->token == T_GENERAL)
       
   804          binary = 0, scan_token(csa);
       
   805       else if (csa->token == T_INTEGER)
       
   806          binary = 0, scan_token(csa);
       
   807       else if (csa->token == T_BINARY)
       
   808          binary = 1, scan_token(csa);
       
   809       else
       
   810          xassert(csa != csa);
       
   811       /* parse list of variables (may be empty) */
       
   812       while (csa->token == T_NAME)
       
   813       {  /* find the corresponding column */
       
   814          j = find_col(csa, csa->image);
       
   815          /* change kind of the variable */
       
   816          glp_set_col_kind(csa->P, j, GLP_IV);
       
   817          /* set 0-1 bounds for the binary variable */
       
   818          if (binary)
       
   819          {  set_lower_bound(csa, j, 0.0);
       
   820             set_upper_bound(csa, j, 1.0);
       
   821          }
       
   822          scan_token(csa);
       
   823       }
       
   824       return;
       
   825 }
       
   826 
       
   827 int glp_read_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname)
       
   828 {     /* read problem data in CPLEX LP format */
       
   829       glp_cpxcp _parm;
       
   830       struct csa _csa, *csa = &_csa;
       
   831       int ret;
       
   832       xprintf("Reading problem data from `%s'...\n", fname);
       
   833       if (parm == NULL)
       
   834          glp_init_cpxcp(&_parm), parm = &_parm;
       
   835       /* check control parameters */
       
   836       check_parm("glp_read_lp", parm);
       
   837       /* initialize common storage area */
       
   838       csa->P = P;
       
   839       csa->parm = parm;
       
   840       csa->fname = fname;
       
   841       csa->fp = NULL;
       
   842       if (setjmp(csa->jump))
       
   843       {  ret = 1;
       
   844          goto done;
       
   845       }
       
   846       csa->count = 0;
       
   847       csa->c = '\n';
       
   848       csa->token = T_EOF;
       
   849       csa->image[0] = '\0';
       
   850       csa->imlen = 0;
       
   851       csa->value = 0.0;
       
   852       csa->n_max = 100;
       
   853       csa->ind = xcalloc(1+csa->n_max, sizeof(int));
       
   854       csa->val = xcalloc(1+csa->n_max, sizeof(double));
       
   855       csa->flag = xcalloc(1+csa->n_max, sizeof(char));
       
   856       memset(&csa->flag[1], 0, csa->n_max * sizeof(char));
       
   857       csa->lb = xcalloc(1+csa->n_max, sizeof(double));
       
   858       csa->ub = xcalloc(1+csa->n_max, sizeof(double));
       
   859       /* erase problem object */
       
   860       glp_erase_prob(P);
       
   861       glp_create_index(P);
       
   862       /* open input CPLEX LP file */
       
   863       csa->fp = xfopen(fname, "r");
       
   864       if (csa->fp == NULL)
       
   865       {  xprintf("Unable to open `%s' - %s\n", fname, xerrmsg());
       
   866          ret = 1;
       
   867          goto done;
       
   868       }
       
   869       /* scan very first token */
       
   870       scan_token(csa);
       
   871       /* parse definition of the objective function */
       
   872       if (!(csa->token == T_MINIMIZE || csa->token == T_MAXIMIZE))
       
   873          error(csa, "`minimize' or `maximize' keyword missing\n");
       
   874       parse_objective(csa);
       
   875       /* parse constraints section */
       
   876       if (csa->token != T_SUBJECT_TO)
       
   877          error(csa, "constraints section missing\n");
       
   878       parse_constraints(csa);
       
   879       /* parse optional bounds section */
       
   880       if (csa->token == T_BOUNDS) parse_bounds(csa);
       
   881       /* parse optional general, integer, and binary sections */
       
   882       while (csa->token == T_GENERAL ||
       
   883              csa->token == T_INTEGER ||
       
   884              csa->token == T_BINARY) parse_integer(csa);
       
   885       /* check for the keyword 'end' */
       
   886       if (csa->token == T_END)
       
   887          scan_token(csa);
       
   888       else if (csa->token == T_EOF)
       
   889          warning(csa, "keyword `end' missing\n");
       
   890       else
       
   891          error(csa, "symbol `%s' in wrong position\n", csa->image);
       
   892       /* nothing must follow the keyword 'end' (except comments) */
       
   893       if (csa->token != T_EOF)
       
   894          error(csa, "extra symbol(s) detected beyond `end'\n");
       
   895       /* set bounds of variables */
       
   896       {  int j, type;
       
   897          double lb, ub;
       
   898          for (j = 1; j <= P->n; j++)
       
   899          {  lb = csa->lb[j];
       
   900             ub = csa->ub[j];
       
   901             if (lb == +DBL_MAX) lb = 0.0;      /* default lb */
       
   902             if (ub == -DBL_MAX) ub = +DBL_MAX; /* default ub */
       
   903             if (lb == -DBL_MAX && ub == +DBL_MAX)
       
   904                type = GLP_FR;
       
   905             else if (ub == +DBL_MAX)
       
   906                type = GLP_LO;
       
   907             else if (lb == -DBL_MAX)
       
   908                type = GLP_UP;
       
   909             else if (lb != ub)
       
   910                type = GLP_DB;
       
   911             else
       
   912                type = GLP_FX;
       
   913             glp_set_col_bnds(csa->P, j, type, lb, ub);
       
   914          }
       
   915       }
       
   916       /* print some statistics */
       
   917       xprintf("%d row%s, %d column%s, %d non-zero%s\n",
       
   918          P->m, P->m == 1 ? "" : "s", P->n, P->n == 1 ? "" : "s",
       
   919          P->nnz, P->nnz == 1 ? "" : "s");
       
   920       if (glp_get_num_int(P) > 0)
       
   921       {  int ni = glp_get_num_int(P);
       
   922          int nb = glp_get_num_bin(P);
       
   923          if (ni == 1)
       
   924          {  if (nb == 0)
       
   925                xprintf("One variable is integer\n");
       
   926             else
       
   927                xprintf("One variable is binary\n");
       
   928          }
       
   929          else
       
   930          {  xprintf("%d integer variables, ", ni);
       
   931             if (nb == 0)
       
   932                xprintf("none");
       
   933             else if (nb == 1)
       
   934                xprintf("one");
       
   935             else if (nb == ni)
       
   936                xprintf("all");
       
   937             else
       
   938                xprintf("%d", nb);
       
   939             xprintf(" of which %s binary\n", nb == 1 ? "is" : "are");
       
   940          }
       
   941       }
       
   942       xprintf("%d lines were read\n", csa->count);
       
   943       /* problem data has been successfully read */
       
   944       glp_delete_index(P);
       
   945       glp_sort_matrix(P);
       
   946       ret = 0;
       
   947 done: if (csa->fp != NULL) xfclose(csa->fp);
       
   948       xfree(csa->ind);
       
   949       xfree(csa->val);
       
   950       xfree(csa->flag);
       
   951       xfree(csa->lb);
       
   952       xfree(csa->ub);
       
   953       if (ret != 0) glp_erase_prob(P);
       
   954       return ret;
       
   955 }
       
   956 
       
   957 /***********************************************************************
       
   958 *  NAME
       
   959 *
       
   960 *  glp_write_lp - write problem data in CPLEX LP format
       
   961 *
       
   962 *  SYNOPSIS
       
   963 *
       
   964 *  int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char
       
   965 *     *fname);
       
   966 *
       
   967 *  DESCRIPTION
       
   968 *
       
   969 *  The routine glp_write_lp writes problem data in CPLEX LP format to
       
   970 *  a text file.
       
   971 *
       
   972 *  The parameter parm is a pointer to the structure glp_cpxcp, which
       
   973 *  specifies control parameters used by the routine. If parm is NULL,
       
   974 *  the routine uses default settings.
       
   975 *
       
   976 *  The character string fname specifies a name of the text file to be
       
   977 *  written.
       
   978 *
       
   979 *  RETURNS
       
   980 *
       
   981 *  If the operation was successful, the routine glp_write_lp returns
       
   982 *  zero. Otherwise, it prints an error message and returns non-zero. */
       
   983 
       
   984 #define csa csa1
       
   985 
       
   986 struct csa
       
   987 {     /* common storage area */
       
   988       glp_prob *P;
       
   989       /* pointer to problem object */
       
   990       const glp_cpxcp *parm;
       
   991       /* pointer to control parameters */
       
   992 };
       
   993 
       
   994 static int check_name(char *name)
       
   995 {     /* check if specified name is valid for CPLEX LP format */
       
   996       if (*name == '.') return 1;
       
   997       if (isdigit((unsigned char)*name)) return 1;
       
   998       for (; *name; name++)
       
   999       {  if (!isalnum((unsigned char)*name) &&
       
  1000              strchr(CHAR_SET, (unsigned char)*name) == NULL) return 1;
       
  1001       }
       
  1002       return 0; /* name is ok */
       
  1003 }
       
  1004 
       
  1005 static void adjust_name(char *name)
       
  1006 {     /* attempt to adjust specified name to make it valid for CPLEX LP
       
  1007          format */
       
  1008       for (; *name; name++)
       
  1009       {  if (*name == ' ')
       
  1010             *name = '_';
       
  1011          else if (*name == '-')
       
  1012             *name = '~';
       
  1013          else if (*name == '[')
       
  1014             *name = '(';
       
  1015          else if (*name == ']')
       
  1016             *name = ')';
       
  1017       }
       
  1018       return;
       
  1019 }
       
  1020 
       
  1021 static char *row_name(struct csa *csa, int i, char rname[255+1])
       
  1022 {     /* construct symbolic name of i-th row (constraint) */
       
  1023       const char *name;
       
  1024       if (i == 0)
       
  1025          name = glp_get_obj_name(csa->P);
       
  1026       else
       
  1027          name = glp_get_row_name(csa->P, i);
       
  1028       if (name == NULL) goto fake;
       
  1029       strcpy(rname, name);
       
  1030       adjust_name(rname);
       
  1031       if (check_name(rname)) goto fake;
       
  1032       return rname;
       
  1033 fake: if (i == 0)
       
  1034          strcpy(rname, "obj");
       
  1035       else
       
  1036          sprintf(rname, "r_%d", i);
       
  1037       return rname;
       
  1038 }
       
  1039 
       
  1040 static char *col_name(struct csa *csa, int j, char cname[255+1])
       
  1041 {     /* construct symbolic name of j-th column (variable) */
       
  1042       const char *name;
       
  1043       name = glp_get_col_name(csa->P, j);
       
  1044       if (name == NULL) goto fake;
       
  1045       strcpy(cname, name);
       
  1046       adjust_name(cname);
       
  1047       if (check_name(cname)) goto fake;
       
  1048       return cname;
       
  1049 fake: sprintf(cname, "x_%d", j);
       
  1050       return cname;
       
  1051 }
       
  1052 
       
  1053 int glp_write_lp(glp_prob *P, const glp_cpxcp *parm, const char *fname)
       
  1054 {     /* write problem data in CPLEX LP format */
       
  1055       glp_cpxcp _parm;
       
  1056       struct csa _csa, *csa = &_csa;
       
  1057       XFILE *fp;
       
  1058       GLPROW *row;
       
  1059       GLPCOL *col;
       
  1060       GLPAIJ *aij;
       
  1061       int i, j, len, flag, count, ret;
       
  1062       char line[1000+1], term[500+1], name[255+1];
       
  1063       xprintf("Writing problem data to `%s'...\n", fname);
       
  1064       if (parm == NULL)
       
  1065          glp_init_cpxcp(&_parm), parm = &_parm;
       
  1066       /* check control parameters */
       
  1067       check_parm("glp_write_lp", parm);
       
  1068       /* initialize common storage area */
       
  1069       csa->P = P;
       
  1070       csa->parm = parm;
       
  1071       /* create output CPLEX LP file */
       
  1072       fp = xfopen(fname, "w"), count = 0;
       
  1073       if (fp == NULL)
       
  1074       {  xprintf("Unable to create `%s' - %s\n", fname, xerrmsg());
       
  1075          ret = 1;
       
  1076          goto done;
       
  1077       }
       
  1078       /* write problem name */
       
  1079       xfprintf(fp, "\\* Problem: %s *\\\n",
       
  1080          P->name == NULL ? "Unknown" : P->name), count++;
       
  1081       xfprintf(fp, "\n"), count++;
       
  1082       /* the problem should contain at least one row and one column */
       
  1083       if (!(P->m > 0 && P->n > 0))
       
  1084       {  xprintf("Warning: problem has no rows/columns\n");
       
  1085          xfprintf(fp, "\\* WARNING: PROBLEM HAS NO ROWS/COLUMNS *\\\n"),
       
  1086             count++;
       
  1087          xfprintf(fp, "\n"), count++;
       
  1088          goto skip;
       
  1089       }
       
  1090       /* write the objective function definition */
       
  1091       if (P->dir == GLP_MIN)
       
  1092          xfprintf(fp, "Minimize\n"), count++;
       
  1093       else if (P->dir == GLP_MAX)
       
  1094          xfprintf(fp, "Maximize\n"), count++;
       
  1095       else
       
  1096          xassert(P != P);
       
  1097       row_name(csa, 0, name);
       
  1098       sprintf(line, " %s:", name);
       
  1099       len = 0;
       
  1100       for (j = 1; j <= P->n; j++)
       
  1101       {  col = P->col[j];
       
  1102          if (col->coef != 0.0 || col->ptr == NULL)
       
  1103          {  len++;
       
  1104             col_name(csa, j, name);
       
  1105             if (col->coef == 0.0)
       
  1106                sprintf(term, " + 0 %s", name); /* empty column */
       
  1107             else if (col->coef == +1.0)
       
  1108                sprintf(term, " + %s", name);
       
  1109             else if (col->coef == -1.0)
       
  1110                sprintf(term, " - %s", name);
       
  1111             else if (col->coef > 0.0)
       
  1112                sprintf(term, " + %.*g %s", DBL_DIG, +col->coef, name);
       
  1113             else
       
  1114                sprintf(term, " - %.*g %s", DBL_DIG, -col->coef, name);
       
  1115             if (strlen(line) + strlen(term) > 72)
       
  1116                xfprintf(fp, "%s\n", line), line[0] = '\0', count++;
       
  1117             strcat(line, term);
       
  1118          }
       
  1119       }
       
  1120       if (len == 0)
       
  1121       {  /* empty objective */
       
  1122          sprintf(term, " 0 %s", col_name(csa, 1, name));
       
  1123          strcat(line, term);
       
  1124       }
       
  1125       xfprintf(fp, "%s\n", line), count++;
       
  1126       if (P->c0 != 0.0)
       
  1127          xfprintf(fp, "\\* constant term = %.*g *\\\n", DBL_DIG, P->c0),
       
  1128             count++;
       
  1129       xfprintf(fp, "\n"), count++;
       
  1130       /* write the constraints section */
       
  1131       xfprintf(fp, "Subject To\n"), count++;
       
  1132       for (i = 1; i <= P->m; i++)
       
  1133       {  row = P->row[i];
       
  1134          if (row->type == GLP_FR) continue; /* skip free row */
       
  1135          row_name(csa, i, name);
       
  1136          sprintf(line, " %s:", name);
       
  1137          /* linear form */
       
  1138          for (aij = row->ptr; aij != NULL; aij = aij->r_next)
       
  1139          {  col_name(csa, aij->col->j, name);
       
  1140             if (aij->val == +1.0)
       
  1141                sprintf(term, " + %s", name);
       
  1142             else if (aij->val == -1.0)
       
  1143                sprintf(term, " - %s", name);
       
  1144             else if (aij->val > 0.0)
       
  1145                sprintf(term, " + %.*g %s", DBL_DIG, +aij->val, name);
       
  1146             else
       
  1147                sprintf(term, " - %.*g %s", DBL_DIG, -aij->val, name);
       
  1148             if (strlen(line) + strlen(term) > 72)
       
  1149                xfprintf(fp, "%s\n", line), line[0] = '\0', count++;
       
  1150             strcat(line, term);
       
  1151          }
       
  1152          if (row->type == GLP_DB)
       
  1153          {  /* double-bounded (ranged) constraint */
       
  1154             sprintf(term, " - ~r_%d", i);
       
  1155             if (strlen(line) + strlen(term) > 72)
       
  1156                xfprintf(fp, "%s\n", line), line[0] = '\0', count++;
       
  1157             strcat(line, term);
       
  1158          }
       
  1159          else if (row->ptr == NULL)
       
  1160          {  /* empty constraint */
       
  1161             sprintf(term, " 0 %s", col_name(csa, 1, name));
       
  1162             strcat(line, term);
       
  1163          }
       
  1164          /* right hand-side */
       
  1165          if (row->type == GLP_LO)
       
  1166             sprintf(term, " >= %.*g", DBL_DIG, row->lb);
       
  1167          else if (row->type == GLP_UP)
       
  1168             sprintf(term, " <= %.*g", DBL_DIG, row->ub);
       
  1169          else if (row->type == GLP_DB || row->type == GLP_FX)
       
  1170             sprintf(term, " = %.*g", DBL_DIG, row->lb);
       
  1171          else
       
  1172             xassert(row != row);
       
  1173          if (strlen(line) + strlen(term) > 72)
       
  1174             xfprintf(fp, "%s\n", line), line[0] = '\0', count++;
       
  1175          strcat(line, term);
       
  1176          xfprintf(fp, "%s\n", line), count++;
       
  1177       }
       
  1178       xfprintf(fp, "\n"), count++;
       
  1179       /* write the bounds section */
       
  1180       flag = 0;
       
  1181       for (i = 1; i <= P->m; i++)
       
  1182       {  row = P->row[i];
       
  1183          if (row->type != GLP_DB) continue;
       
  1184          if (!flag)
       
  1185             xfprintf(fp, "Bounds\n"), flag = 1, count++;
       
  1186          xfprintf(fp, " 0 <= ~r_%d <= %.*g\n",
       
  1187             i, DBL_DIG, row->ub - row->lb), count++;
       
  1188       }
       
  1189       for (j = 1; j <= P->n; j++)
       
  1190       {  col = P->col[j];
       
  1191          if (col->type == GLP_LO && col->lb == 0.0) continue;
       
  1192          if (!flag)
       
  1193             xfprintf(fp, "Bounds\n"), flag = 1, count++;
       
  1194          col_name(csa, j, name);
       
  1195          if (col->type == GLP_FR)
       
  1196             xfprintf(fp, " %s free\n", name), count++;
       
  1197          else if (col->type == GLP_LO)
       
  1198             xfprintf(fp, " %s >= %.*g\n",
       
  1199                name, DBL_DIG, col->lb), count++;
       
  1200          else if (col->type == GLP_UP)
       
  1201             xfprintf(fp, " -Inf <= %s <= %.*g\n",
       
  1202                name, DBL_DIG, col->ub), count++;
       
  1203          else if (col->type == GLP_DB)
       
  1204             xfprintf(fp, " %.*g <= %s <= %.*g\n",
       
  1205                DBL_DIG, col->lb, name, DBL_DIG, col->ub), count++;
       
  1206          else if (col->type == GLP_FX)
       
  1207             xfprintf(fp, " %s = %.*g\n",
       
  1208                name, DBL_DIG, col->lb), count++;
       
  1209          else
       
  1210             xassert(col != col);
       
  1211       }
       
  1212       if (flag) xfprintf(fp, "\n"), count++;
       
  1213       /* write the integer section */
       
  1214       flag = 0;
       
  1215       for (j = 1; j <= P->n; j++)
       
  1216       {  col = P->col[j];
       
  1217          if (col->kind == GLP_CV) continue;
       
  1218          xassert(col->kind == GLP_IV);
       
  1219          if (!flag)
       
  1220             xfprintf(fp, "Generals\n"), flag = 1, count++;
       
  1221          xfprintf(fp, " %s\n", col_name(csa, j, name)), count++;
       
  1222       }
       
  1223       if (flag) xfprintf(fp, "\n"), count++;
       
  1224 skip: /* write the end keyword */
       
  1225       xfprintf(fp, "End\n"), count++;
       
  1226       xfflush(fp);
       
  1227       if (xferror(fp))
       
  1228       {  xprintf("Write error on `%s' - %s\n", fname, xerrmsg());
       
  1229          ret = 1;
       
  1230          goto done;
       
  1231       }
       
  1232       /* problem data has been successfully written */
       
  1233       xprintf("%d lines were written\n", count);
       
  1234       ret = 0;
       
  1235 done: if (fp != NULL) xfclose(fp);
       
  1236       return ret;
       
  1237 }
       
  1238 
       
  1239 /* eof */