1 
2 #include <string.h>
3 #include <stdarg.h>
4 #include <ctype.h>
5 #include "commonlib.h"
6 #include "lp_lib.h"
7 #include "lp_scale.h"
8 #include "lp_report.h"
9 #include "lp_MPS.h"
10 
11 #ifdef FORTIFY
12 # include "lp_fortify.h"
13 #endif
14 
15 /* Define buffer-size controled function mapping */
16 # if defined _MSC_VER
17 #  define vsnprintf _vsnprintf
18 # endif
19 
20 /* MPS file input and output routines for lp_solve                           */
21 /* ------------------------------------------------------------------------- */
22 
23 /*
24 A:  MPS format was named after an early IBM LP product and has emerged
25 as a de facto standard ASCII medium among most of the commercial LP
26 codes.  Essentially all commercial LP codes accept this format, but if
27 you are using public domain software and have MPS files, you may need
28 to write your own reader routine for this.  It's not too hard.  The
29 main things to know about MPS format are that it is column oriented (as
30 opposed to entering the model as equations), and everything (variables,
31 rows, etc.) gets a name.  MPS format is described in more detail in
32 Murtagh's book, referenced in another section. Also,
33 
34 ftp://softlib.cs.rice.edu/pub/miplib/mps_format
35 
36 is a nice short introduction.  exports
37 
38 MPS is an old format, so it is set up as though you were using punch
39 cards, and is not free format. Fields start in column 1, 5, 15, 25, 40
40 and 50.  Sections of an MPS file are marked by so-called header cards,
41 which are distinguished by their starting in column 1.  Although it is
42 typical to use upper-case throughout the file (like I said, MPS has
43 long historical roots), many MPS-readers will accept mixed-case for
44 anything except the header cards, and some allow mixed-case anywhere.
45 The names that you choose for the individual entities (constraints or
46 variables) are not important to the solver; you should pick names that
47 are meaningful to you, or will be easy for a post-processing code to
48 read.
49 
50 Here is a little sample model written in MPS format (explained in more
51 detail below):
52 
53 NAME          TESTPROB
54 ROWS
55  N  COST
56  L  LIM1
57  G  LIM2
58  E  MYEQN
59 COLUMNS
60     XONE      COST                 1   LIM1                 1
61     XONE      LIM2                 1
62     YTWO      COST                 4   LIM1                 1
63     YTWO      MYEQN               -1
64     ZTHREE    COST                 9   LIM2                 1
65     ZTHREE    MYEQN                1
66 RHS
67     RHS1      LIM1                 5   LIM2                10
68     RHS1      MYEQN                7
69 BOUNDS
70  UP BND1      XONE                 4
71  LO BND1      YTWO                -1
72  UP BND1      YTWO                 1
73 ENDATA
74 
75 means:
76 
77 Optimize
78  COST:    XONE + 4 YTWO + 9 ZTHREE
79 Subject To
80  LIM1:    XONE + YTWO <= 5
81  LIM2:    XONE + ZTHREE >= 10
82  MYEQN:   - YTWO + ZTHREE  = 7
83 Bounds
84  0 <= XONE <= 4
85 -1 <= YTWO <= 1
86 End
87 
88 */
89 
90 /* copy a MPS name, only trailing spaces are removed. In MPS, names can have
91    embedded spaces! */
namecpy(char * into,char * from)92 STATIC void namecpy(char *into, char *from)
93 {
94   int i;
95 
96   /* copy at most 8 characters of from, stop at end of string or newline */
97   for(i = 0; (from[i] != '\0') && (from[i] != '\n') && (from[i] != '\r') && (i < 8); i++)
98     into[i] = from[i];
99 
100   /* end with end of string */
101   into[i] = '\0';
102 
103   /* remove trailing spaces, if any */
104   for(i--; (i >= 0) && (into[i] == ' '); i--)
105     into[i] = '\0';
106 }
107 
108 /* scan an MPS line, and pick up the information in the fields that are
109    present */
110 
111 /* scan_line for fixed MPS format */
scan_lineFIXED(int section,char * line,char * field1,char * field2,char * field3,double * field4,char * field5,double * field6)112 STATIC int scan_lineFIXED(int section, char* line, char *field1, char *field2, char *field3,
113                           double *field4, char *field5, double *field6)
114 {
115   int  items = 0, line_len;
116   char buf[16], *ptr1, *ptr2;
117 
118   line_len = (int) strlen(line);
119   while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' ')))
120    line_len--;
121 
122   if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */
123     strncpy(buf, line, 4);
124     buf[4] = '\0';
125     sscanf(buf, "%s", field1);
126     items++;
127   }
128   else
129     field1[0] = '\0';
130 
131   line += 4;
132 
133   if(line_len >= 5) { /* name */
134     namecpy(field2, line);
135     items++;
136   }
137   else
138     field2[0] = '\0';
139 
140   line += 10;
141 
142   if(line_len >= 14) { /* name */
143     namecpy(field3, line);
144     items++;
145   }
146   else
147     field3[0] = '\0';
148 
149   line += 10;
150 
151   if(line_len >= 25) { /* number */
152     strncpy(buf, line, 15);
153     buf[15] = '\0';
154     for(ptr1 = ptr2 = buf; ; ptr1++)
155       if(!isspace((unsigned char) *ptr1))
156         if((*(ptr2++) = *ptr1) == 0)
157           break;
158     /* *field4 = atof(buf); */
159     *field4 = strtod(buf, &ptr1);
160     if(*ptr1)
161       return(-1);
162     items++;
163   }
164   else
165     *field4 = 0;
166 
167   line += 15;
168 
169   if(line_len >= 40) { /* name */
170     namecpy(field5, line);
171     items++;
172   }
173   else
174     field5[0] = '\0';
175   line += 10;
176 
177   if(line_len >= 50) { /* number */
178     strncpy(buf, line, 15);
179     buf[15] = '\0';
180     for(ptr1 = ptr2 = buf; ; ptr1++)
181       if(!isspace((unsigned char) *ptr1))
182         if((*(ptr2++) = *ptr1) == 0)
183           break;
184     /* *field6 = atof(buf); */
185     *field6 = strtod(buf, &ptr1);
186     if(*ptr1)
187       return(-1);
188     items++;
189   }
190   else
191     *field6 = 0;
192 
193   return(items);
194 }
195 
spaces(char * line,int line_len)196 STATIC int spaces(char *line, int line_len)
197 {
198   int l;
199   char *line1 = line;
200 
201   while (*line1 == ' ')
202     line1++;
203   l = (int) (line1 - line);
204   if (line_len < l)
205     l = line_len;
206   return(l);
207 }
208 
lenfield(char * line,int line_len)209 STATIC int lenfield(char *line, int line_len)
210 {
211   int l;
212   char *line1 = line;
213 
214   while ((*line1) && (*line1 != ' '))
215     line1++;
216   l = (int) (line1 - line);
217   if (line_len < l)
218     l = line_len;
219   return(l);
220 }
221 
222 /* scan_line for fixed MPS format */
scan_lineFREE(int section,char * line,char * field1,char * field2,char * field3,double * field4,char * field5,double * field6)223 STATIC int scan_lineFREE(int section, char* line, char *field1, char *field2, char *field3,
224                          double *field4, char *field5, double *field6)
225 {
226   int  items = 0, line_len, len;
227   char buf[256], *ptr1, *ptr2;
228 
229   line_len = (int) strlen(line);
230   while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' ')))
231    line_len--;
232 
233   len = spaces(line, line_len);
234   line += len;
235   line_len -= len;
236 
237   if ((section == MPSCOLUMNS) || (section == MPSRHS) || (section == MPSRANGES)) {
238     field1[0] = '\0';
239     items++;
240   }
241   else {
242     len = lenfield(line, line_len);
243     if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */
244       strncpy(buf, line, len);
245       buf[len] = '\0';
246       sscanf(buf, "%s", field1);
247       items++;
248     }
249     else
250       field1[0] = '\0';
251 
252     line += len;
253     line_len -= len;
254 
255     len = spaces(line, line_len);
256     line += len;
257     line_len -= len;
258   }
259 
260   len = lenfield(line, line_len);
261   if(line_len >= 1) { /* name */
262     strncpy(field2, line, len);
263     field2[len] = '\0';
264     items++;
265   }
266   else
267     field2[0] = '\0';
268 
269   line += len;
270   line_len -= len;
271 
272   len = spaces(line, line_len);
273   line += len;
274   line_len -= len;
275 
276   len = lenfield(line, line_len);
277   if(line_len >= 1) { /* name */
278     strncpy(field3, line, len);
279     field3[len] = '\0';
280     items++;
281   }
282   else
283     field3[0] = '\0';
284 
285   line += len;
286   line_len -= len;
287 
288   len = spaces(line, line_len);
289   line += len;
290   line_len -= len;
291 
292   if (*field3) {
293     if((section == MPSCOLUMNS) && (strcmp(field3, "'MARKER'") == 0)) {
294       *field4 = 0;
295       items++;
296       ptr1 = field3;
297     }
298     else {
299       *field4 = strtod(field3, &ptr1);
300       if(*ptr1 == 0) {
301         strcpy(field3, field2);
302   if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */)
303     *field2 = 0;
304   else {
305           strcpy(field2, field1);
306     *field1 = 0;
307   }
308         items++;
309       }
310       else
311         ptr1 = NULL;
312     }
313   }
314   else ptr1 = NULL;
315 
316   if(ptr1 == NULL) {
317     len = lenfield(line, line_len);
318     if(line_len >= 1) { /* number */
319       strncpy(buf, line, len);
320       buf[len] = '\0';
321       for(ptr1 = ptr2 = buf; ; ptr1++)
322         if(!isspace((unsigned char) *ptr1))
323           if((*(ptr2++) = *ptr1) == 0)
324             break;
325       /* *field4 = atof(buf); */
326       *field4 = strtod(buf, &ptr1);
327       if(*ptr1)
328         return(-1);
329       items++;
330     }
331     else
332       *field4 = 0;
333 
334     line += len;
335     line_len -= len;
336 
337     len = spaces(line, line_len);
338     line += len;
339     line_len -= len;
340   }
341 
342   len = lenfield(line, line_len);
343   if(line_len >= 1) { /* name */
344     strncpy(field5, line, len);
345     field5[len] = '\0';
346     items++;
347   }
348   else
349     field5[0] = '\0';
350   line += len;
351   line_len -= len;
352 
353   len = spaces(line, line_len);
354   line += len;
355   line_len -= len;
356 
357   len = lenfield(line, line_len);
358   if(line_len >= 1) { /* number */
359     strncpy(buf, line, len);
360     buf[len] = '\0';
361     for(ptr1 = ptr2 = buf; ; ptr1++)
362       if(!isspace((unsigned char) *ptr1))
363         if((*(ptr2++) = *ptr1) == 0)
364           break;
365     /* *field6 = atof(buf); */
366     *field6 = strtod(buf, &ptr1);
367     if(*ptr1)
368       return(-1);
369     items++;
370   }
371   else
372     *field6 = 0;
373 
374   if((section == MPSSOS) && (items == 2)) {
375     strcpy(field3, field2);
376     strcpy(field2, field1);
377     *field1 = 0;
378   }
379 
380   if(section != MPSOBJNAME) {
381     for(ptr1 = field1; *ptr1; ptr1++)
382       *ptr1=(char)toupper(*ptr1);
383   }
384 
385   return(items);
386 }
387 
addmpscolumn(lprec * lp,MYBOOL Int_section,MYBOOL * Column_ready,int * count,REAL * Last_column,int * Last_columnno,char * Last_col_name)388 STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, MYBOOL *Column_ready,
389                         int *count, REAL *Last_column, int *Last_columnno, char *Last_col_name)
390 {
391   int ok = TRUE;
392 
393   if (*Column_ready) {
394     ok = add_columnex(lp, *count, Last_column, Last_columnno);
395     if (ok) {
396       ok = set_col_name(lp, lp->columns, Last_col_name);
397     }
398     if (ok)
399       set_int(lp, lp->columns, Int_section);
400   }
401   *Column_ready = FALSE;
402   *count = 0;
403   return(ok);
404 }
405 
appendmpsitem(int * count,int rowIndex[],REAL rowValue[])406 STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], REAL rowValue[])
407 {
408   int i = *count;
409 
410   if(rowValue[i] == 0)
411     return( FALSE );
412 
413   while((i > 0) && (rowIndex[i] < rowIndex[i-1])) {
414     swapINT (rowIndex+i, rowIndex+i-1);
415     swapREAL(rowValue+i, rowValue+i-1);
416     i--;
417   }
418   (*count)++;
419   return( TRUE );
420 }
421 
MPS_readfile(lprec ** newlp,char * filename,int typeMPS,int verbose)422 MYBOOL MPS_readfile(lprec **newlp, char *filename, int typeMPS, int verbose)
423 {
424   MYBOOL status = FALSE;
425   FILE   *fpin;
426 
427   fpin = fopen(filename, "r");
428   if(fpin != NULL) {
429     status = MPS_readhandle(newlp, fpin, typeMPS, verbose);
430     fclose(fpin);
431   }
432   return( status );
433 }
434 
MPS_input(void * fpin,char * buf,int max_size)435 static int __WINAPI MPS_input(void *fpin, char *buf, int max_size)
436 {
437   return(fgets(buf, max_size, (FILE *) fpin) != NULL);
438 }
439 
MPS_readhandle(lprec ** newlp,FILE * filehandle,int typeMPS,int verbose)440 MYBOOL __WINAPI MPS_readhandle(lprec **newlp, FILE *filehandle, int typeMPS, int verbose)
441 {
442   return(MPS_readex(newlp, (void *) filehandle, MPS_input, typeMPS, verbose));
443 }
444 
MPS_readex(lprec ** newlp,void * userhandle,read_modeldata_func read_modeldata,int typeMPS,int verbose)445 MYBOOL __WINAPI MPS_readex(lprec **newlp, void *userhandle, read_modeldata_func read_modeldata, int typeMPS, int verbose)
446 {
447   char   field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], line[BUFSIZ], tmp[BUFSIZ],
448          Last_col_name[BUFSIZ], probname[BUFSIZ], OBJNAME[BUFSIZ], *ptr;
449   int    items, row, Lineno, var,
450          section = MPSUNDEF, variant = 0, NZ = 0, SOS = 0;
451   MYBOOL Int_section, Column_ready, Column_ready1,
452          Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE;
453   double field4, field6;
454   REAL   *Last_column = NULL;
455   int    count = 0, *Last_columnno = NULL;
456   int    OBJSENSE = ROWTYPE_EMPTY;
457   lprec  *lp;
458   int    (*scan_line)(int section, char* line, char *field1, char *field2, char *field3,
459                       double *field4, char *field5, double *field6);
460 
461   if(newlp == NULL)
462     return( CompleteStatus );
463   else if(*newlp == NULL)
464     lp = make_lp(0, 0);
465   else
466     lp = *newlp;
467 
468   switch(typeMPS) {
469     case MPSFIXED:
470       scan_line = scan_lineFIXED;
471       break;
472     case MPSFREE:
473       scan_line = scan_lineFREE;
474       break;
475     default:
476       report(lp, IMPORTANT, "MPS_readfile: Unrecognized MPS line type.\n");
477       delete_lp(lp);
478       return( CompleteStatus );
479   }
480 
481   if (lp != NULL) {
482     lp->source_is_file = TRUE;
483     lp->verbose = verbose;
484     strcpy(Last_col_name, "");
485     strcpy(OBJNAME, "");
486     Int_section = FALSE;
487     Column_ready = FALSE;
488     Lineno = 0;
489 
490     /* let's initialize line to all zero's */
491     MEMCLEAR(line, BUFSIZ);
492 
493     while(read_modeldata(userhandle, line, BUFSIZ - 1)) {
494       Lineno++;
495 
496       for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
497 
498       /* skip lines which start with "*", they are comment */
499       if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) {
500         report(lp, FULL, "Comment on line %d: %s", Lineno, line);
501         continue;
502       }
503 
504       report(lp, FULL, "Line %6d: %s", Lineno, line);
505 
506       /* first check for "special" lines: NAME, ROWS, BOUNDS .... */
507       /* this must start in the first position of line */
508       if(line[0] != ' ') {
509         sscanf(line, "%s", tmp);
510         if(strcmp(tmp, "NAME") == 0) {
511           section = MPSNAME;
512           *probname = 0;
513           sscanf(line, "NAME %s", probname);
514           if (!set_lp_name(lp, probname))
515             break;
516         }
517         else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJSENSE") == 0)) {
518           section = MPSOBJSENSE;
519           report(lp, FULL, "Switching to OBJSENSE section\n");
520         }
521         else if((typeMPS == MPSFREE) && (strcmp(tmp, "OBJNAME") == 0)) {
522           section = MPSOBJNAME;
523           report(lp, FULL, "Switching to OBJNAME section\n");
524         }
525         else if(strcmp(tmp, "ROWS") == 0) {
526           section = MPSROWS;
527           report(lp, FULL, "Switching to ROWS section\n");
528         }
529         else if(strcmp(tmp, "COLUMNS") == 0) {
530           allocREAL(lp, &Last_column, lp->rows + 1, TRUE);
531           allocINT(lp, &Last_columnno, lp->rows + 1, TRUE);
532           count = 0;
533           if ((Last_column == NULL) || (Last_columnno == NULL))
534             break;
535           section = MPSCOLUMNS;
536           report(lp, FULL, "Switching to COLUMNS section\n");
537         }
538         else if(strcmp(tmp, "RHS") == 0) {
539           if (!addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name))
540             break;
541           section = MPSRHS;
542           report(lp, FULL, "Switching to RHS section\n");
543         }
544         else if(strcmp(tmp, "BOUNDS") == 0) {
545           section = MPSBOUNDS;
546           report(lp, FULL, "Switching to BOUNDS section\n");
547         }
548         else if(strcmp(tmp, "RANGES") == 0) {
549           section = MPSRANGES;
550           report(lp, FULL, "Switching to RANGES section\n");
551         }
552         else if((strcmp(tmp, "SOS") == 0) || (strcmp(tmp, "SETS") == 0)) {
553           section = MPSSOS;
554           if(strcmp(tmp, "SOS") == 0)
555             variant = 0;
556           else
557             variant = 1;
558           report(lp, FULL, "Switching to %s section\n", tmp);
559         }
560         else if(strcmp(tmp, "ENDATA") == 0) {
561           report(lp, FULL, "Finished reading MPS file\n");
562           CompleteStatus = TRUE;
563           break;
564         }
565         else { /* line does not start with space and does not match above */
566           report(lp, IMPORTANT, "Unrecognized MPS line %d: %s\n", Lineno, line);
567           break;
568         }
569       }
570       else { /* normal line, process */
571         items = scan_line(section, line, field1, field2, field3, &field4, field5, &field6);
572         if(items < 0){
573           report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line);
574           break;
575         }
576 
577         switch(section) {
578 
579         case MPSNAME:
580           report(lp, IMPORTANT, "Error, extra line under NAME line\n");
581           break;
582 
583         case MPSOBJSENSE:
584           if(OBJSENSE != ROWTYPE_EMPTY) {
585             report(lp, IMPORTANT, "Error, extra line under OBJSENSE line\n");
586             break;
587           }
588           if((strcmp(field1, "MAXIMIZE") == 0) || (strcmp(field1, "MAX") == 0)) {
589             OBJSENSE = ROWTYPE_OFMAX;
590             set_maxim(lp);
591           }
592           else if((strcmp(field1, "MINIMIZE") == 0) || (strcmp(field1, "MIN") == 0)) {
593             OBJSENSE = ROWTYPE_OFMIN;
594             set_minim(lp);
595           }
596           else {
597             report(lp, SEVERE, "Unknown OBJSENSE direction '%s' on line %d\n", field1, Lineno);
598             break;
599           }
600           continue;
601 
602         case MPSOBJNAME:
603           if(*OBJNAME) {
604             report(lp, IMPORTANT, "Error, extra line under OBJNAME line\n");
605             break;
606           }
607           strcpy(OBJNAME, field1);
608           continue;
609 
610         /* Process entries in the ROWS section */
611         case MPSROWS:
612           /* field1: rel. operator; field2: name of constraint */
613 
614           report(lp, FULL, "Row   %5d: %s %s\n", lp->rows + 1, field1, field2);
615 
616           if(strcmp(field1, "N") == 0) {
617             if((*OBJNAME) && (strcmp(field2, OBJNAME)))
618               /* Ignore this objective name since it is not equal to the OBJNAME name */;
619             else if(!OF_found) { /* take the first N row as OF, ignore others */
620               if (!set_row_name(lp, 0, field2))
621                 break;
622               OF_found = TRUE;
623             }
624             else if(!Unconstrained_rows_found) {
625               report(lp, IMPORTANT, "Unconstrained row %s ignored\n", field2);
626               report(lp, IMPORTANT, "Further messages of this kind will be suppressed\n");
627               Unconstrained_rows_found = TRUE;
628             }
629           }
630           else if(strcmp(field1, "L") == 0) {
631             if ((!str_add_constraint(lp, "" ,LE ,0)) || (!set_row_name(lp, lp->rows, field2)))
632               break;
633           }
634           else if(strcmp(field1, "G") == 0) {
635             if ((!str_add_constraint(lp, "" ,GE ,0)) || (!set_row_name(lp, lp->rows, field2)))
636               break;
637           }
638           else if(strcmp(field1, "E") == 0) {
639             if ((!str_add_constraint(lp, "",EQ ,0)) || (!set_row_name(lp, lp->rows, field2)))
640               break;
641           }
642           else {
643             report(lp, SEVERE, "Unknown relation code '%s' on line %d\n", field1, Lineno);
644             break;
645           }
646 
647           continue;
648 
649         /* Process entries in the COLUMNS section */
650         case MPSCOLUMNS:
651           /* field2: variable; field3: constraint; field4: coef */
652           /* optional: field5: constraint; field6: coef */
653 
654           report(lp, FULL, "Column %4d: %s %s %g %s %g\n",
655                             lp->columns + 1, field2, field3, field4, field5, field6);
656 
657           if((items == 4) || (items == 5) || (items == 6)) {
658             if (NZ == 0)
659               strcpy(Last_col_name, field2);
660             else if(*field2) {
661               Column_ready1 = (MYBOOL) (strcmp(field2, Last_col_name) != 0);
662               if(Column_ready1) {
663                 if (find_var(lp, field2, FALSE) >= 0) {
664                   report(lp, SEVERE, "Variable name (%s) is already used!\n", field2);
665                   break;
666                 }
667 
668                 if(Column_ready) {  /* Added ability to handle non-standard "same as above" column name */
669                   if (addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) {
670                     strcpy(Last_col_name, field2);
671                     NZ = 0;
672                   }
673                   else
674                     break;
675                 }
676               }
677             }
678             if(items == 5) { /* there might be an INTEND or INTORG marker */
679              /* look for "    <name>  'MARKER'                 'INTORG'"
680                       or "    <name>  'MARKER'                 'INTEND'"  */
681               if(strcmp(field3, "'MARKER'") != 0)
682                 break;
683               if(strcmp(field5, "'INTORG'") == 0) {
684                 Int_section = TRUE;
685                 report(lp, FULL, "Switching to integer section\n");
686               }
687               else if(strcmp(field5, "'INTEND'") == 0) {
688                 Int_section = FALSE;
689                 report(lp, FULL, "Switching to non-integer section\n");
690               }
691               else
692                 report(lp, IMPORTANT, "Unknown marker (ignored) at line %d: %s\n",
693                                        Lineno, field5);
694             }
695             else if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
696               if(row > lp->rows)
697                 report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field3);
698               Last_columnno[count] = row;
699               Last_column[count] = (REAL)field4;
700               if(appendmpsitem(&count, Last_columnno, Last_column)) {
701                 NZ++;
702                 Column_ready = TRUE;
703               }
704             }
705           }
706           if(items == 6) {
707             if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
708               if(row > lp->rows)
709                 report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field5);
710               Last_columnno[count] = row;
711               Last_column[count] = (REAL)field6;
712               if(appendmpsitem(&count, Last_columnno, Last_column)) {
713                 NZ++;
714                 Column_ready = TRUE;
715               }
716             }
717           }
718 
719           if((items < 4) || (items > 6)) { /* Wrong! */
720             report(lp, CRITICAL, "Wrong number of items (%d) in COLUMNS section (line %d)\n",
721                                   items, Lineno);
722             break;
723           }
724 
725           continue;
726 
727         /* Process entries in the RHS section */
728         /* field2: uninteresting name; field3: constraint name */
729         /* field4: value */
730         /* optional: field5: constraint name; field6: value */
731         case MPSRHS:
732 
733           report(lp, FULL, "RHS line: %s %s %g %s %g\n",
734                             field2, field3, field4, field5, field6);
735 
736           if((items != 4) && (items != 6)) {
737             report(lp, CRITICAL, "Wrong number of items (%d) in RHS section line %d\n",
738                                   items, Lineno);
739             break;
740           }
741 
742           if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
743             set_rh(lp, row, (REAL)field4);
744           }
745 
746           if(items == 6) {
747             if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
748               set_rh(lp, row, (REAL)field6);
749             }
750           }
751 
752           continue;
753 
754         /* Process entries in the BOUNDS section */
755         /* field1: bound type; field2: uninteresting name; */
756         /* field3: variable name; field4: value */
757         case MPSBOUNDS:
758 
759           report(lp, FULL, "BOUNDS line: %s %s %s %g\n",
760                             field1, field2, field3, field4);
761 
762           var = find_var(lp, field3, FALSE);
763           if(var < 0){ /* bound on undefined var in COLUMNS section ... */
764             Column_ready = TRUE;
765             if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field3))
766               break;
767             Column_ready = TRUE;
768             var = find_var(lp, field3, TRUE);
769           }
770           if(var < 0) /* undefined var and could add ... */;
771           else if(strcmp(field1, "UP") == 0) {
772           /* upper bound */
773             if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
774               break;
775           }
776           else if(strcmp(field1, "SC") == 0) {
777             /* upper bound */
778             if(field4 == 0)
779               field4 = lp->infinite;
780             if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
781               break;
782             set_semicont(lp, var, TRUE);
783           }
784           else if(strcmp(field1, "SI") == 0) {
785             /* upper bound */
786             if(field4 == 0)
787               field4 = lp->infinite;
788             if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
789               break;
790             set_int(lp, var, TRUE);
791             set_semicont(lp, var, TRUE);
792           }
793           else if(strcmp(field1, "LO") == 0) {
794             /* lower bound */
795             if(!set_bounds(lp, var, field4, get_upbo(lp, var)))
796               break;
797           }
798 	  else if(strcmp(field1, "PL") == 0) { /* plus-ranged variable */
799             if(!set_bounds(lp, var, get_lowbo(lp, var), lp->infinite))
800               break;
801 	  }
802           else if(strcmp(field1, "MI") == 0) { /* minus-ranged variable */
803             if(!set_bounds(lp, var, -lp->infinite, get_upbo(lp, var)))
804               break;
805           }
806           else if(strcmp(field1, "FR") == 0) { /* free variable */
807             set_unbounded(lp, var);
808           }
809           else if(strcmp(field1, "FX") == 0) {
810             /* fixed, upper _and_ lower  */
811             if(!set_bounds(lp, var, field4, field4))
812               break;
813           }
814           else if(strcmp(field1, "BV") == 0) { /* binary variable */
815             set_binary(lp, var, TRUE);
816           }
817           /* AMPL bounds type UI and LI added by E.Imamura (CRIEPI)  */
818           else if(strcmp(field1, "UI") == 0) { /* upper bound for integer variable */
819             if(!set_bounds(lp, var, get_lowbo(lp, var), field4))
820               break;
821             set_int(lp, var, TRUE);
822           }
823           else if(strcmp(field1, "LI") == 0) { /* lower bound for integer variable - corrected by KE */
824             if(!set_bounds(lp, var, field4, get_upbo(lp, var)))
825               break;
826             set_int(lp, var, TRUE);
827           }
828           else {
829             report(lp, CRITICAL, "BOUND type %s on line %d is not supported",
830                                   field1, Lineno);
831             break;
832           }
833 
834           continue;
835 
836           /* Process entries in the BOUNDS section */
837 
838       /* We have to implement the following semantics:
839 
840       D. The RANGES section is for constraints of the form: h <=
841       constraint <= u .  The range of the constraint is r = u - h .  The
842       value of r is specified in the RANGES section, and the value of u or
843       h is specified in the RHS section.  If b is the value entered in the
844       RHS section, and r is the value entered in the RANGES section, then
845       u and h are thus defined:
846 
847       row type       sign of r       h          u
848       ----------------------------------------------
849      G            + or -         b        b + |r|
850      L            + or -       b - |r|      b
851      E              +            b        b + |r|
852      E              -          b - |r|      b            */
853 
854         /* field2: uninteresting name; field3: constraint name */
855         /* field4: value */
856         /* optional: field5: constraint name; field6: value */
857 
858         case MPSRANGES:
859 
860           report(lp, FULL, "RANGES line: %s %s %g %s %g",
861                             field2, field3, field4, field5, field6);
862 
863           if((items != 4) && (items != 6)) {
864             report(lp, CRITICAL, "Wrong number of items (%d) in RANGES section line %d",
865                                   items, Lineno);
866             break;
867           }
868 
869           if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) {
870             /* Determine constraint type */
871 
872             if(fabs(field4) >= lp->infinite) {
873               report(lp, IMPORTANT,
874                           "Warning, Range for row %s >= infinity (value %g) on line %d, ignored",
875                           field3, field4, Lineno);
876             }
877             else if(field4 == 0) {
878               /* Change of a GE or LE to EQ */
879               if(lp->orig_upbo[row] != 0)
880                 set_constr_type(lp, row, EQ);
881             }
882             else if(is_chsign(lp, row)) {
883               /* GE */
884               lp->orig_upbo[row] = fabs(field4);
885             }
886             else if((lp->orig_upbo[row] == 0) && (field4 >= 0)) {
887               /*  EQ with positive sign of r value */
888               set_constr_type(lp, row, GE);
889               lp->orig_upbo[row] = field4;
890             }
891             else if(lp->orig_upbo[row] == lp->infinite) {
892               /* LE */
893               lp->orig_upbo[row] = fabs(field4);
894             }
895             else if((lp->orig_upbo[row] == 0) && (field4 < 0)) {
896               /* EQ with negative sign of r value */
897               set_constr_type(lp, row, LE);
898               lp->orig_upbo[row] = my_flipsign(field4);
899             }
900             else { /* let's be paranoid */
901               report(lp, IMPORTANT,
902                           "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d",
903                           row, is_chsign(lp, row), (double)lp->orig_upbo[row], Lineno);
904             }
905           }
906 
907           if(items == 6) {
908             if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) {
909               /* Determine constraint type */
910 
911               if(fabs(field6) >= lp->infinite) {
912                 report(lp, IMPORTANT,
913                             "Warning, Range for row %s >= infinity (value %g) on line %d, ignored",
914                             field5, field6, Lineno);
915               }
916               else if(field6 == 0) {
917                 /* Change of a GE or LE to EQ */
918                 if(lp->orig_upbo[row] != 0)
919                   set_constr_type(lp, row, EQ);
920               }
921               else if(is_chsign(lp, row)) {
922                 /* GE */
923                 lp->orig_upbo[row] = fabs(field6);
924               }
925               else if(lp->orig_upbo[row] == 0 && field6 >= 0) {
926                 /*  EQ with positive sign of r value */
927                 set_constr_type(lp, row, GE);
928                 lp->orig_upbo[row] = field6;
929               }
930               else if(lp->orig_upbo[row] == lp->infinite) {
931                 /* LE */
932                 lp->orig_upbo[row] = fabs(field6);
933               }
934               else if((lp->orig_upbo[row] == 0) && (field6 < 0)) {
935                 /* EQ with negative sign of r value */
936                 set_constr_type(lp, row, LE);
937                 lp->orig_upbo[row] = my_flipsign(field6);
938               }
939               else { /* let's be paranoid */
940                 report(lp, IMPORTANT,
941                             "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d",
942                             row, is_chsign(lp,row), (double) lp->orig_upbo[row], Lineno);
943               }
944             }
945           }
946 
947           continue;
948 
949         /* Process entries in the SOS section */
950 
951         /* We have to implement the following semantics:
952 
953           E. The SOS section is for ordered variable sets of the form:
954       x1, x2, x3 ... xn where only a given number of consequtive variables
955           may be non-zero.  Each set definition is prefaced by type, name
956       and priority data.  Each set member has an optional weight that
957       determines its order.  There are two forms supported; a full format
958       and a reduced CPLEX-like format.                                       */
959 
960         case MPSSOS:
961           report(lp, FULL, "SOS line: %s %s %g %s %g",
962                              field2, field3, field4, field5, field6);
963 
964           if((items == 0) || (items > 4)) {
965             report(lp, IMPORTANT,
966                    "Invalid number of items (%d) in SOS section line %d\n",
967                    items, Lineno);
968             break;
969           }
970 
971           if(strlen(field1) == 0) items--;  /* fix scanline anomoly! */
972 
973           /* Check if this is the start of a new SOS */
974           if(items == 1 || items == 4) {
975             row = (int) (field1[1] - '0');
976             if((row <= 0) || (row > 9)) {
977               report(lp, IMPORTANT,
978                      "Error: Invalid SOS type %s line %d\n", field1, Lineno);
979               break;
980             }
981             field1[0] = '\0';               /* fix scanline anomoly! */
982 
983             /* lp_solve needs a name for the SOS */
984             if(variant == 0) {
985               if(strlen(field3) == 0)  /* CPLEX format does not provide a SOS name; create one */
986                 sprintf(field3, "SOS_%d", SOS_count(lp) + 1);
987             }
988             else {                     /* Remap XPRESS format name */
989               strcpy(field3, field1);
990             }
991             /* Obtain the SOS priority */
992             if(items == 4)
993               SOS = (int) field4;
994             else
995               SOS = 1;
996 
997             /* Define a new SOS instance */
998 
999             SOS = add_SOS(lp, field3, (int) row, SOS, 0, NULL, NULL);
1000           }
1001           /* Otherwise, add set members to the active SOS */
1002           else {
1003             char *field = (items == 3) ? field3 /* Native lp_solve and XPRESS formats */ : field2 /* CPLEX format */;
1004 
1005             var = find_var(lp, field, FALSE);  /* Native lp_solve and XPRESS formats */
1006             if(var < 0){ /* SOS on undefined var in COLUMNS section ... */
1007               Column_ready = TRUE;
1008               if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field))
1009                 break;
1010               Column_ready = TRUE;
1011               var = find_var(lp, field, TRUE);
1012             }
1013             if((var < 0) || (SOS < 1)) /* undefined var and could add ... */;
1014             else append_SOSrec(lp->SOS->sos_list[SOS-1], 1, &var, &field4);
1015           }
1016 
1017           continue;
1018         }
1019 
1020         /* If we got here there was an error "upstream" */
1021          report(lp, IMPORTANT,
1022                      "Error: Cannot handle line %d\n", Lineno);
1023          break;
1024       }
1025     }
1026 
1027     if((*OBJNAME) && (!OF_found)) {
1028       report(lp, IMPORTANT,
1029                   "Error: Objective function specified by OBJNAME card not found\n");
1030       CompleteStatus = FALSE;
1031     }
1032 
1033     if(CompleteStatus == FALSE)
1034       delete_lp(lp);
1035     else
1036       *newlp = lp;
1037     if(Last_column != NULL)
1038       FREE(Last_column);
1039     if(Last_columnno != NULL)
1040       FREE(Last_columnno);
1041   }
1042 
1043   return( CompleteStatus );
1044 }
1045 
number(char * str,REAL value)1046 static void number(char *str,REAL value)
1047  {
1048   char __str[80], *_str;
1049   int  i;
1050 
1051   /* sprintf(_str,"%12.6G",value); */
1052   _str=__str+2;
1053   if (value>=0.0)
1054    if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) {
1055     int n=15;
1056 
1057     do {
1058      n--;
1059      i=sprintf(_str,"%*.*E",n,n-6,(double) value);
1060      if (i>12) {
1061       char *ptr=strchr(_str,'E');
1062 
1063       if (ptr!=NULL) {
1064        if (*(++ptr)=='-') ptr++;
1065        while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) {
1066         strcpy(ptr,ptr+1);
1067         i--;
1068        }
1069       }
1070      }
1071     } while (i>12);
1072    }
1073    else if (value>=1.0e10) {
1074     int n=13;
1075 
1076     do {
1077      i=sprintf(_str,"%*.0f",--n,(double) value);
1078     } while (i>12);
1079    }
1080    else {
1081     if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) {
1082      for (i=11;i>=0;i--)
1083       if (_str[i]!='.') {
1084        if (++_str[i]>'9') _str[i]='0';
1085        else break;
1086       }
1087      if (i<0) {
1088       *(--_str)='1';
1089       *(--_str)=' ';
1090      }
1091     }
1092    }
1093   else
1094    if ((value<-0.99999999e11) || (value>-0.0001)) {
1095     int n=15;
1096 
1097     do {
1098      n--;
1099      i=sprintf(_str,"%*.*E",n,n-7,(double) value);
1100      if (i>12) {
1101       char *ptr=strchr(_str,'E');
1102 
1103       if (ptr!=NULL) {
1104        if (*(++ptr)=='-') ptr++;
1105        while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) {
1106         strcpy(ptr,ptr+1);
1107         i--;
1108        }
1109       }
1110      }
1111     } while (i>12);
1112    }
1113    else if (value<=-1.0e9) {
1114     int n=13;
1115 
1116     do {
1117      i=sprintf(_str,"%*.0f",--n,(double) value);
1118     } while (i>12);
1119    }
1120    else
1121     if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) {
1122      for (i=11;i>=1;i--)
1123       if (_str[i]!='.') {
1124        if (++_str[i]>'9') _str[i]='0';
1125        else break;
1126       }
1127      if (i<1) {
1128       *_str='1';
1129       *(--_str)='-';
1130       *(--_str)=' ';
1131      }
1132     }
1133   _str[12] ='\0'; strcpy(str, _str);
1134   // strncpy(str,_str,12); str[12] = '\0';
1135  }
1136 
1137 static char numberbuffer[15];
1138 
formatnumber12(double a)1139 static char *formatnumber12(double a)
1140 {
1141 #if 0
1142   return(sprintf(numberbuffer, "%12g", a));
1143 #else
1144   number(numberbuffer, a);
1145   return(numberbuffer);
1146 #endif
1147 }
1148 
MPSnameFIXED(char * name)1149 STATIC char *MPSnameFIXED(char *name)
1150 {
1151   static char name0[9];
1152 
1153   sprintf(name0, "%-8.8s", name);
1154   return(name0);
1155 }
1156 
MPSnameFREE(char * name)1157 STATIC char *MPSnameFREE(char *name)
1158 {
1159   if(strlen(name) < 8)
1160     return(MPSnameFIXED(name));
1161   else
1162     return(name);
1163 }
1164 
write_data(void * userhandle,write_modeldata_func write_modeldata,char * format,...)1165 static void write_data(void *userhandle, write_modeldata_func write_modeldata, char *format, ...)
1166 {
1167   char buff[DEF_STRBUFSIZE+1];
1168   va_list ap;
1169 
1170   va_start(ap, format);
1171   vsnprintf(buff, DEF_STRBUFSIZE, format, ap);
1172   write_modeldata(userhandle, buff);
1173   va_end(ap);
1174 }
1175 
MPS_writefileex(lprec * lp,int typeMPS,void * userhandle,write_modeldata_func write_modeldata)1176 MYBOOL MPS_writefileex(lprec *lp, int typeMPS, void *userhandle, write_modeldata_func write_modeldata)
1177 {
1178   int    i, j, jj, je, k, marker, putheader, ChangeSignObj = FALSE, *idx, *idx1;
1179   MYBOOL ok = TRUE, names_used;
1180   REAL   a, *val, *val1;
1181 /* "Output" not defined. Excluded April 19 2006 SEB. */
1182 /*  FILE   *output = stdout; */
1183   char * (*MPSname)(char *name);
1184 
1185   if(lp->matA->is_roworder) {
1186     report(lp, IMPORTANT, "MPS_writefile: Cannot write to MPS file while in row entry mode.\n");
1187     return(FALSE);
1188   }
1189 
1190   switch(typeMPS) {
1191     case MPSFIXED:
1192       MPSname = MPSnameFIXED;
1193       ChangeSignObj = is_maxim(lp);
1194       break;
1195     case MPSFREE:
1196       MPSname = MPSnameFREE;
1197       break;
1198     default:
1199       report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS name type.\n");
1200       return(FALSE);
1201   }
1202 
1203   names_used = lp->names_used;
1204 
1205   if(typeMPS == MPSFIXED) {
1206     /* Check if there is no variable name where the first 8 charachters are equal to the first 8 characters of anothe variable */
1207     if(names_used)
1208       for(i = 1; (i <= lp->columns) && (ok); i++)
1209         if((lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL) && (!is_splitvar(lp, i)) && (strlen(lp->col_name[i]->name) > 8))
1210           for(j = 1; (j < i) && (ok); j++)
1211     if((lp->col_name[j] != NULL) && (lp->col_name[j]->name != NULL) && (!is_splitvar(lp, j)))
1212       if(strncmp(lp->col_name[i]->name, lp->col_name[j]->name, 8) == 0)
1213         ok = FALSE;
1214   }
1215 
1216   if(!ok) {
1217     lp->names_used = FALSE;
1218     ok = TRUE;
1219   }
1220   marker = 0;
1221 
1222   /* First write metadata in structured comment form (lp_solve style) */
1223   write_data(userhandle, write_modeldata, "*<meta creator='lp_solve v%d.%d'>\n",
1224                   (int) MAJORVERSION, (int) MINORVERSION);
1225   write_data(userhandle, write_modeldata, "*<meta rows=%d>\n", lp->rows);
1226   write_data(userhandle, write_modeldata, "*<meta columns=%d>\n", lp->columns);
1227   write_data(userhandle, write_modeldata, "*<meta equalities=%d>\n", lp->equalities);
1228   if(SOS_count(lp) > 0)
1229     write_data(userhandle, write_modeldata, "*<meta SOS=%d>\n", SOS_count(lp));
1230   write_data(userhandle, write_modeldata, "*<meta integers=%d>\n", lp->int_vars);
1231   if(lp->sc_vars > 0)
1232     write_data(userhandle, write_modeldata, "*<meta scvars=%d>\n", lp->sc_vars);
1233   write_data(userhandle, write_modeldata, "*<meta origsense='%s'>\n", (is_maxim(lp) ? "MAX" : "MIN"));
1234   write_data(userhandle, write_modeldata, "*\n");
1235 
1236   /* Write the MPS content */
1237   write_data(userhandle, write_modeldata, "NAME          %s\n", MPSname(get_lp_name(lp)));
1238   if((typeMPS == MPSFREE) && (is_maxim(lp)))
1239     write_data(userhandle, write_modeldata, "OBJSENSE\n MAX\n");
1240   write_data(userhandle, write_modeldata, "ROWS\n");
1241   for(i = 0; i <= lp->rows; i++) {
1242     if(i == 0)
1243       write_data(userhandle, write_modeldata, " N  ");
1244     else if(lp->orig_upbo[i] != 0) {
1245       if(is_chsign(lp,i))
1246         write_data(userhandle, write_modeldata, " G  ");
1247       else
1248         write_data(userhandle, write_modeldata, " L  ");
1249     }
1250     else
1251       write_data(userhandle, write_modeldata, " E  ");
1252     write_data(userhandle, write_modeldata, "%s\n", MPSname(get_row_name(lp, i)));
1253   }
1254 
1255   allocREAL(lp, &val, 1 + lp->rows, TRUE);
1256   allocINT(lp, &idx, 1 + lp->rows, TRUE);
1257   write_data(userhandle, write_modeldata, "COLUMNS\n");
1258   for(i = 1; i <= lp->columns; i++) {
1259     if(!is_splitvar(lp, i)) {
1260       if(is_int(lp,i) && (marker % 2) == 0) {
1261         write_data(userhandle, write_modeldata, "    MARK%04d  'MARKER'                 'INTORG'\n",
1262                 marker);
1263         marker++;
1264       }
1265       if(!is_int(lp,i) && (marker % 2) == 1) {
1266         write_data(userhandle, write_modeldata, "    MARK%04d  'MARKER'                 'INTEND'\n",
1267                 marker);
1268         marker++;
1269       }
1270 
1271       /* Loop over non-zero column entries */
1272       je = get_columnex(lp, i, val, idx);
1273       for(k = 1, val1 = val, idx1 = idx, jj = 0; jj < je; jj++) {
1274         k = 1 - k;
1275         j = *(idx1++);
1276         a = *(val1++);
1277         if (k == 0) {
1278           write_data(userhandle, write_modeldata, "    %s",
1279                           MPSname(get_col_name(lp, i)));
1280           write_data(userhandle, write_modeldata, "  %s  %s",
1281                           MPSname(get_row_name(lp, j)),
1282 /*                          formatnumber12((double) a)); */
1283                           formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1))));
1284 	}
1285         else
1286           write_data(userhandle, write_modeldata, "   %s  %s\n",
1287                           MPSname(get_row_name(lp, j)),
1288                           formatnumber12((double) (a * (j == 0 && ChangeSignObj ? -1 : 1))));
1289 /*                          formatnumber12((double) a)); */
1290       }
1291       if(k == 0)
1292         write_data(userhandle, write_modeldata, "\n");
1293     }
1294   }
1295   if((marker % 2) == 1) {
1296     write_data(userhandle, write_modeldata, "    MARK%04d  'MARKER'                 'INTEND'\n",
1297             marker);
1298   /* marker++; */ /* marker not used after this */
1299   }
1300   FREE(idx);
1301   FREE(val);
1302 
1303   write_data(userhandle, write_modeldata, "RHS\n");
1304   for(k = 1, i = 0; i <= lp->rows; i++) {
1305     a = lp->orig_rhs[i];
1306     if(a) {
1307       a = unscaled_value(lp, a, i);
1308       if((i == 0) || is_chsign(lp, i))
1309         a = my_flipsign(a);
1310       k = 1 - k;
1311       if(k == 0)
1312         write_data(userhandle, write_modeldata, "    RHS       %s  %s",
1313                         MPSname(get_row_name(lp, i)),
1314                         formatnumber12((double)a));
1315       else
1316         write_data(userhandle, write_modeldata, "   %s  %s\n",
1317                         MPSname(get_row_name(lp, i)),
1318                         formatnumber12((double)a));
1319     }
1320   }
1321   if(k == 0)
1322     write_data(userhandle, write_modeldata, "\n");
1323 
1324   putheader = TRUE;
1325   for(k = 1, i = 1; i <= lp->rows; i++){
1326     a = 0;
1327     if((lp->orig_upbo[i] < lp->infinite) && (lp->orig_upbo[i] != 0.0))
1328       a = lp->orig_upbo[i];
1329     if(a) {
1330       if(putheader) {
1331         write_data(userhandle, write_modeldata, "RANGES\n");
1332         putheader = FALSE;
1333       }
1334       a = unscaled_value(lp, a, i);
1335       k = 1 - k;
1336       if(k == 0)
1337         write_data(userhandle, write_modeldata, "    RGS       %s  %s",
1338                         MPSname(get_row_name(lp, i)),
1339                         formatnumber12((double)a));
1340       else
1341         write_data(userhandle, write_modeldata, "   %s  %s\n",
1342                         MPSname(get_row_name(lp, i)),
1343                         formatnumber12((double)a));
1344     }
1345   }
1346   if(k == 0)
1347     write_data(userhandle, write_modeldata, "\n");
1348 
1349   putheader = TRUE;
1350   for(i = lp->rows + 1; i <= lp->sum; i++)
1351     if(!is_splitvar(lp, i - lp->rows)) {
1352       j = i - lp->rows;
1353       if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) &&
1354          (lp->orig_lowbo[i] == lp->orig_upbo[i])) {
1355         a = lp->orig_upbo[i];
1356         a = unscaled_value(lp, a, i);
1357         if(putheader) {
1358           write_data(userhandle, write_modeldata, "BOUNDS\n");
1359           putheader = FALSE;
1360         }
1361         write_data(userhandle, write_modeldata, " FX BND       %s  %s\n",
1362                         MPSname(get_col_name(lp, j)),
1363                         formatnumber12((double)a));
1364       }
1365       else if(is_binary(lp, j)) {
1366         if(putheader) {
1367           write_data(userhandle, write_modeldata, "BOUNDS\n");
1368           putheader = FALSE;
1369         }
1370         write_data(userhandle, write_modeldata, " BV BND       %s\n",
1371                         MPSname(get_col_name(lp, j)));
1372       }
1373       else if(is_unbounded(lp, j)) {
1374         if(putheader) {
1375           write_data(userhandle, write_modeldata, "BOUNDS\n");
1376           putheader = FALSE;
1377         }
1378         write_data(userhandle, write_modeldata, " FR BND       %s\n",
1379                         MPSname(get_col_name(lp, j)));
1380       }
1381       else {
1382         if(lp->orig_upbo[i] < lp->infinite) {
1383           a = lp->orig_upbo[i];
1384           a = unscaled_value(lp, a, i);
1385           if(putheader) {
1386             write_data(userhandle, write_modeldata, "BOUNDS\n");
1387             putheader = FALSE;
1388           }
1389           if(is_semicont(lp, j)) {
1390             if(is_int(lp, j))
1391               write_data(userhandle, write_modeldata, " SI BND       %s  %s\n",
1392                               MPSname(get_col_name(lp, j)),
1393                               formatnumber12((double)a));
1394             else
1395               write_data(userhandle, write_modeldata, " SC BND       %s  %s\n",
1396                               MPSname(get_col_name(lp, j)),
1397             formatnumber12((double)a));
1398           }
1399           else
1400             write_data(userhandle, write_modeldata, " UP BND       %s  %s\n",
1401                             MPSname(get_col_name(lp, j)),
1402                             formatnumber12((double)a));
1403         }
1404         if(lp->orig_lowbo[i] != 0) {
1405           a = lp->orig_lowbo[i];
1406           a = unscaled_value(lp, a, i);
1407           if(putheader) {
1408             write_data(userhandle, write_modeldata, "BOUNDS\n");
1409             putheader = FALSE;
1410           }
1411           if(lp->orig_lowbo[i] != -lp->infinite)
1412             write_data(userhandle, write_modeldata, " LO BND       %s  %s\n",
1413                             MPSname(get_col_name(lp, j)),
1414                             formatnumber12((double)a));
1415           else
1416             write_data(userhandle, write_modeldata, " MI BND       %s\n",
1417                             MPSname(get_col_name(lp, j)));
1418         }
1419       }
1420     }
1421 
1422  /* Write optional SOS section */
1423   putheader = TRUE;
1424   for(i = 0; i < SOS_count(lp); i++) {
1425     SOSgroup *SOS = lp->SOS;
1426 
1427     if(putheader) {
1428       write_data(userhandle, write_modeldata, "SOS\n");
1429       putheader = FALSE;
1430     }
1431     write_data(userhandle, write_modeldata, " S%1d SOS       %s  %s\n",
1432                     SOS->sos_list[i]->type,
1433                     MPSname(SOS->sos_list[i]->name),
1434                     formatnumber12((double) SOS->sos_list[i]->priority));
1435     for(j = 1; j <= SOS->sos_list[i]->size; j++) {
1436       write_data(userhandle, write_modeldata, "    SOS       %s  %s\n",
1437                       MPSname(get_col_name(lp, SOS->sos_list[i]->members[j])),
1438                       formatnumber12((double) SOS->sos_list[i]->weights[j]));
1439     }
1440   }
1441 
1442   write_data(userhandle, write_modeldata, "ENDATA\n");
1443 
1444   lp->names_used = names_used;
1445 
1446   return(ok);
1447 }
1448 
write_lpdata(void * userhandle,char * buf)1449 static int __WINAPI write_lpdata(void *userhandle, char *buf)
1450 {
1451   fputs(buf, (FILE *) userhandle);
1452   return(TRUE);
1453 }
1454 
MPS_writefile(lprec * lp,int typeMPS,char * filename)1455 MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename)
1456 {
1457   FILE *output; /* = stdout; */
1458   MYBOOL ok;
1459 
1460   ok = ((output = fopen(filename, "w")) != NULL);
1461   if(!ok)
1462     return(ok);
1463 
1464   ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata);
1465 
1466   fclose(output);
1467 
1468   return(ok);
1469 }
1470 
MPS_writehandle(lprec * lp,int typeMPS,FILE * output)1471 MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output)
1472 {
1473   MYBOOL ok;
1474 
1475   set_outputstream(lp, output);
1476 
1477   output = lp->outstream;
1478 
1479   ok = MPS_writefileex(lp, typeMPS, (void *) output, write_lpdata);
1480 
1481   return(ok);
1482 }
1483 
1484 
1485 /* Read and write BAS files */
1486 /* #define OldNameMatch */
1487 #ifdef OldNameMatch
MPS_getnameidx(lprec * lp,char * varname,MYBOOL isrow)1488 static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL isrow)
1489 {
1490   int in = -1;
1491 
1492   in = get_nameindex(lp, varname, isrow);
1493   if((in < 0) && (strncmp(varname, (isrow ? ROWNAMEMASK : COLNAMEMASK), 1) == 0)) {
1494     if(sscanf(varname + 1, "%d", &in) != 1)
1495       in = -1;
1496   }
1497   return( in );
1498 }
1499 #else
MPS_getnameidx(lprec * lp,char * varname,MYBOOL tryrowfirst)1500 static int MPS_getnameidx(lprec *lp, char *varname, MYBOOL tryrowfirst)
1501 {
1502   int in;
1503 
1504   /* Have we defined our own variable names? */
1505   if(lp->names_used) {
1506     /* First check the primary name list */
1507     in = get_nameindex(lp, varname, tryrowfirst);
1508     if((in > 0) && !tryrowfirst)
1509       in += lp->rows;
1510     /* If we were unsuccessful, try the secondary name list */
1511     else if(in < 0) {
1512       in = get_nameindex(lp, varname, (MYBOOL) !tryrowfirst);
1513       if((in > 0) && tryrowfirst)
1514         in += lp->rows;
1515     }
1516   }
1517   /* If not, see if we can match the standard name mask */
1518   else {
1519     if(strncmp(varname, (tryrowfirst ? ROWNAMEMASK : COLNAMEMASK), 1) == 0) {
1520       /* Fail if we did not successfully scan as a valid integer */
1521       if((sscanf(varname + 1, "%d", &in) != 1) ||
1522          (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns)))
1523         in = -1;
1524     }
1525     else if(strncmp(varname, (!tryrowfirst ? ROWNAMEMASK : COLNAMEMASK), 1) == 0) {
1526       /* Fail if we did not successfully scan as a valid integer */
1527       if((sscanf(varname + 1, "%d", &in) != 1) ||
1528          (in < (tryrowfirst ? 0 : 1)) || (in > (tryrowfirst ? lp->rows : lp->columns)))
1529         in = -1;
1530     }
1531   }
1532   return( in );
1533 }
1534 #endif
1535 
MPS_readBAS(lprec * lp,int typeMPS,char * filename,char * info)1536 MYBOOL MPS_readBAS(lprec *lp, int typeMPS, char *filename, char *info)
1537 {
1538   char   field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ],
1539          line[BUFSIZ], tmp[BUFSIZ], *ptr;
1540   double field4, field6;
1541   int    ib, in, items, Lineno = 0;
1542   MYBOOL ok;
1543   FILE   *input = stdin;
1544   int    (*scan_line)(int section, char* line, char *field1, char *field2, char *field3,
1545                       double *field4, char *field5, double *field6);
1546 
1547   switch(typeMPS) {
1548     case MPSFIXED:
1549       scan_line = scan_lineFIXED;
1550       break;
1551     case MPSFREE:
1552       scan_line = scan_lineFREE;
1553       break;
1554     default:
1555       report(lp, IMPORTANT, "MPS_readBAS: unrecognized MPS line type.\n");
1556       return(FALSE);
1557   }
1558 
1559   ok = (MYBOOL) ((filename != NULL) && ((input = fopen(filename,"r")) != NULL));
1560   if(!ok)
1561     return(ok);
1562   default_basis(lp);
1563 
1564   /* Let's initialize line to all zero's */
1565   MEMCLEAR(line, BUFSIZ);
1566   ok = FALSE;
1567   while(fgets(line, BUFSIZ - 1, input)) {
1568     Lineno++;
1569 
1570     for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
1571 
1572     /* skip lines which start with "*", they are comment */
1573     if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) {
1574       report(lp, FULL, "Comment on line %d: %s", Lineno, line);
1575       continue;
1576     }
1577 
1578     report(lp, FULL, "Line %6d: %s", Lineno, line);
1579 
1580     /* first check for "special" lines: in our case only NAME and ENDATA,
1581        ...this must start in the first position of line */
1582     if(line[0] != ' ') {
1583       sscanf(line, "%s", tmp);
1584       if(strcmp(tmp, "NAME") == 0) {
1585         if(info != NULL) {
1586           *info = 0;
1587           for(ptr = line + 4; (*ptr) && (isspace((unsigned char) *ptr)); ptr++);
1588           in = (int) strlen(ptr);
1589           while ((in > 0) && ((ptr[in - 1] == '\r') || (ptr[in - 1] == '\n') || isspace(ptr[in - 1])))
1590             in--;
1591           ptr[in] = 0;
1592           strcpy(info, ptr);
1593         }
1594       }
1595       else if(strcmp(tmp, "ENDATA") == 0) {
1596         report(lp, FULL, "Finished reading BAS file\n");
1597         ok = TRUE;
1598         break;
1599       }
1600       else { /* line does not start with space and does not match above */
1601         report(lp, IMPORTANT, "Unrecognized BAS line %d: %s\n", Lineno, line);
1602         break;
1603       }
1604     }
1605     else { /* normal line, process */
1606       items = scan_line(MPSRHS, line, field1, field2, field3, &field4, field5, &field6);
1607       if(items < 0){
1608         report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line);
1609         break;
1610       }
1611       /* find first variable index value */
1612       in = MPS_getnameidx(lp, field2, FALSE);
1613 #ifdef OldNameMatch
1614       if(in < 0)
1615         in = MPS_getnameidx(lp, field2, TRUE);
1616       else
1617         in += lp->rows;
1618 #endif
1619       if(in < 0)
1620         break;
1621 
1622       /* check if we have the basic/non-basic variable format */
1623       if(field1[0] == 'X') {
1624         /* find second variable index value */
1625         ib = in;
1626         in = MPS_getnameidx(lp, field3, FALSE);
1627 #ifdef OldNameMatch
1628         if(in < 0)
1629           in = MPS_getnameidx(lp, field3, TRUE);
1630         else
1631           in += lp->rows;
1632 #endif
1633         if(in < 0)
1634           break;
1635 
1636         lp->is_lower[in] = (MYBOOL) (field1[1] == 'L');
1637         lp->is_basic[ib] = TRUE;
1638       }
1639       else
1640         lp->is_lower[in] = (MYBOOL) (field1[0] == 'L');
1641 
1642       lp->is_basic[in] = FALSE;
1643 
1644     }
1645   }
1646   /* Update the basis index-to-variable array */
1647   ib = 0;
1648   items = lp->sum;
1649   for(in = 1; in <= items; in++)
1650     if(lp->is_basic[in]) {
1651       ib++;
1652       lp->var_basic[ib] = in;
1653     }
1654 
1655   fclose(input);
1656   return( ok );
1657 }
1658 
MPS_writeBAS(lprec * lp,int typeMPS,char * filename)1659 MYBOOL MPS_writeBAS(lprec *lp, int typeMPS, char *filename)
1660 {
1661   int    ib, in;
1662   MYBOOL ok;
1663   char   name1[100], name2[100];
1664   FILE   *output; /*  = stdout; */
1665   char * (*MPSname)(char *name);
1666 
1667   /* Set name formatter */
1668   switch(typeMPS) {
1669     case MPSFIXED:
1670       MPSname = MPSnameFIXED;
1671       break;
1672     case MPSFREE:
1673       MPSname = MPSnameFREE;
1674       break;
1675     default:
1676       report(lp, IMPORTANT, "MPS_writeBAS: unrecognized MPS name type.\n");
1677       return(FALSE);
1678   }
1679 
1680   /* Open the file for writing */
1681   ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL));
1682   if(!ok)
1683     return(ok);
1684   if(filename == NULL && lp->outstream != NULL)
1685     output = lp->outstream;
1686 
1687   fprintf(output, "NAME          %s Rows %d Cols %d Iters %.0f\n",
1688                   get_lp_name(lp), lp->rows, lp->columns, (double) get_total_iter(lp));
1689 
1690   ib = lp->rows;
1691   in = 0;
1692   while ((ib < lp->sum) || (in < lp->sum)) {
1693 
1694     /* Find next basic variable (skip slacks) */
1695     ib++;
1696     while((ib <= lp->sum) && !lp->is_basic[ib])
1697       ib++;
1698 
1699     /* Find next non-basic variable (skip lower-bounded structural variables) */
1700     in++;
1701     while((in <= lp->sum) && (lp->is_basic[in] ||
1702                               ((in > lp->rows) && lp->is_lower[in])))
1703       in++;
1704 
1705     /* Check if we have a basic/non-basic variable pair */
1706     if((ib <= lp->sum) && (in <= lp->sum)) {
1707       strcpy(name1, MPSname((ib <= lp->rows ? get_row_name(lp, ib) :
1708                                               get_col_name(lp, ib-lp->rows))));
1709       strcpy(name2, MPSname((in <= lp->rows ? get_row_name(lp, in) :
1710                                               get_col_name(lp, in-lp->rows))));
1711       fprintf(output, " %2s %s  %s\n", (lp->is_lower[in] ? "XL" : "XU"), name1, name2);
1712     }
1713 
1714     /* Otherwise just write the bound state of the non-basic variable */
1715     else if(in <= lp->sum) {
1716       strcpy(name1, MPSname((in <= lp->rows ? get_row_name(lp, in) :
1717                                               get_col_name(lp, in-lp->rows))));
1718       fprintf(output, " %2s %s\n", (lp->is_lower[in] ? "LL" : "UL"), name1);
1719     }
1720 
1721   }
1722   fprintf(output, "ENDATA\n");
1723 
1724   if(filename != NULL)
1725     fclose(output);
1726   return( ok );
1727 }
1728