src/glpcpx.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
     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 */