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