Scippy

SoPlex

Sequential object-oriented simPlex

mpsinput.cpp
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the class library */
4/* SoPlex --- the Sequential object-oriented simPlex. */
5/* */
6/* Copyright (c) 1996-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SoPlex; see the file LICENSE. If not email to soplex@zib.de. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file mpsinput.cpp
26 * @brief Read MPS format files.
27 */
28
29#include <assert.h>
30#include <ctype.h>
31#include <string.h>
32
33#include "soplex/spxdefines.h"
34#include "soplex/mpsinput.h"
35#include "soplex/spxout.h"
36
37#define PATCH_CHAR '_'
38#define BLANK ' '
39
40namespace soplex
41{
42
43/// fill the line from \p pos up to column 80 with blanks.
44static void clear_from(char* buf, int pos)
45{
46 for(int i = pos; i < 80; i++)
47 buf[i] = BLANK;
48
49 buf[80] = '\0';
50}
51
52/// change all blanks inside a field to #PATCH_CHAR.
53static void patch_field(char* buf, int beg, int end)
54{
55 while((beg <= end) && (buf[end] == BLANK))
56 end--;
57
58 while((beg <= end) && (buf[beg] == BLANK))
59 beg++;
60
61 for(int i = beg; i <= end; i++)
62 if(buf[i] == BLANK)
63 buf[i] = PATCH_CHAR;
64}
65
66/// read a MPS format data line and parse the fields.
68{
69 int len;
70 int space;
71 char* s;
72 bool is_marker;
73 bool is_comment;
74
75 do
76 {
77 m_f0 = m_f1 = m_f2 = m_f3 = m_f4 = m_f5 = nullptr;
78 is_marker = false;
79
80 // Read until we have a non-empty, non-comment line.
81 do
82 {
83 if(!m_input.getline(m_buf, sizeof(m_buf)).good() && !m_input.eof())
84 return false;
85
86 m_lineno++;
87
88 SPxOut::debug(this, "DMPSIN01 Line {} {}\n", m_lineno, m_buf);
89
90 /* check if comment line */
91 is_comment = true;
92
93 if(m_buf[0] == '*')
94 continue;
95
96 /* normalize line and check if it is empty */
97 len = int(strlen(m_buf));
98
99 for(int i = 0; i < len; i++)
100 {
101 if(m_buf[i] == '\t' || m_buf[i] == '\n' || m_buf[i] == '\r')
102 m_buf[i] = BLANK;
103 else if(m_buf[i] != BLANK)
104 is_comment = false;
105 }
106 }
107 while(is_comment);
108
109 len = int(strlen(m_buf));
110
111 if(len < 80)
112 clear_from(m_buf, len);
113
114 assert(strlen(m_buf) >= 80);
115
116 /* Look for new section
117 */
118 if(*m_buf != BLANK)
119 {
120 m_f0 = strtok(&m_buf[0], " ");
121
122 assert(m_f0 != nullptr);
123
124 m_f1 = strtok(nullptr, " ");
125
126 return true;
127 }
128
129 if(!m_is_new_format)
130 {
131 /* Test for fixed format comments
132 */
133 if((m_buf[14] == '$') && (m_buf[13] == ' '))
134 clear_from(m_buf, 14);
135 else if((m_buf[39] == '$') && (m_buf[38] == ' '))
136 clear_from(m_buf, 39);
137
138 /* Test for fixed format
139 */
140 space = m_buf[3]
141 | m_buf[12] | m_buf[13]
142 | m_buf[22] | m_buf[23]
143 | m_buf[36] | m_buf[37] | m_buf[38]
144 | m_buf[47] | m_buf[48]
145 | m_buf[61] | m_buf[62] | m_buf[63];
146
147 if(space == BLANK)
148 {
149 /* Now we have space at the right positions.
150 * But are there also the non space where they
151 * should be ?
152 */
153 bool number = isdigit(m_buf[24]) || isdigit(m_buf[25])
154 || isdigit(m_buf[26]) || isdigit(m_buf[27])
155 || isdigit(m_buf[28]) || isdigit(m_buf[29])
156 || isdigit(m_buf[30]) || isdigit(m_buf[31])
157 || isdigit(m_buf[32]) || isdigit(m_buf[33])
158 || isdigit(m_buf[34]) || isdigit(m_buf[35]);
159
160 /* len < 13 is handle ROW lines with embedded spaces
161 * in the names correctly
162 */
163 if((m_section != ROWS && number) || (m_section == ROWS && len < 13))
164 {
165 /* Now we assume fixed format, so we patch possible embedded spaces.
166 */
167 patch_field(m_buf, 4, 12);
168 patch_field(m_buf, 14, 22);
169 patch_field(m_buf, 39, 47);
170 }
171 else
172 {
173 m_is_new_format = true;
174 }
175 }
176 else
177 {
178 m_is_new_format = true;
179 }
180 }
181
182 s = &m_buf[1];
183
184 /* At this point it is not clear if we have a indicator field.
185 * If there is none (e.g. empty) f1 will be the first name field.
186 * If there is one, f2 will be the first name field.
187 *
188 * Initially comment marks '$' ar only allowed in the beginning
189 * of the 2nd and 3rd name field. We test all fields but the first.
190 * This makes no difference, since if the $ is at the start of a value
191 * field, the line will be errornous anyway.
192 */
193 do
194 {
195 if(nullptr == (m_f1 = strtok(s, " ")))
196 break;
197
198 if((nullptr == (m_f2 = strtok(nullptr, " "))) || (*m_f2 == '$'))
199 {
200 m_f2 = nullptr;
201 break;
202 }
203
204 if(!strcmp(m_f2, "'MARKER'"))
205 is_marker = true;
206
207 if((nullptr == (m_f3 = strtok(nullptr, " "))) || (*m_f3 == '$'))
208 {
209 m_f3 = nullptr;
210 break;
211 }
212
213 if(is_marker)
214 {
215 if(!strcmp(m_f3, "'INTORG'"))
216 m_is_integer = true;
217 else if(!strcmp(m_f3, "'INTEND'"))
218 m_is_integer = false;
219 else
220 break; // unknown marker
221 }
222
223 if(!strcmp(m_f3, "'MARKER'"))
224 is_marker = true;
225
226 if((nullptr == (m_f4 = strtok(nullptr, " "))) || (*m_f4 == '$'))
227 {
228 m_f4 = nullptr;
229 break;
230 }
231
232 if(is_marker)
233 {
234 if(!strcmp(m_f4, "'INTORG'"))
235 m_is_integer = true;
236 else if(!strcmp(m_f4, "'INTEND'"))
237 m_is_integer = false;
238 else
239 break; // unknown marker
240 }
241
242 if((nullptr == (m_f5 = strtok(nullptr, " "))) || (*m_f5 == '$'))
243 m_f5 = nullptr;
244 }
245 while(false);
246 }
247 while(is_marker);
248
249 SPxOut::debug(this, "DMPSIN02 -----------------------------------------------\n"
250 "DMPSIN03 f0={}\n"
251 "DMPSIN04 f1={}\n"
252 "DMPSIN05 f2={}\n"
253 "DMPSIN07 f4={}\n"
254 "DMPSIN06 f3={}\n"
255 "DMPSIN08 f5={}\n"
256 "DMPSIN09 -----------------------------------------------\n",
257 ((m_f0 == nullptr) ? "nil" : m_f0),
258 ((m_f1 == nullptr) ? "nil" : m_f1),
259 ((m_f2 == nullptr) ? "nil" : m_f2),
260 ((m_f3 == nullptr) ? "nil" : m_f3),
261 ((m_f4 == nullptr) ? "nil" : m_f4),
262 ((m_f5 == nullptr) ? "nil" : m_f5));
263 return true;
264}
265
266/// Insert \p name as field 1 and shift all other fields up.
267void MPSInput::insertName(const char* name, bool second)
268{
269 m_f5 = m_f4;
270 m_f4 = m_f3;
271 m_f3 = m_f2;
272
273 if(second)
274 m_f2 = name;
275 else
276 {
277 m_f2 = m_f1;
278 m_f1 = name;
279 }
280}
281
282} // namespace soplex
char m_buf[MAX_LINE_LEN]
the line buffer
Definition: mpsinput.h:85
const char * m_f5
sixth field in a line
Definition: mpsinput.h:97
const char * m_f1
second field in a line
Definition: mpsinput.h:89
Section m_section
Definition: mpsinput.h:75
std::istream & m_input
the input stream from which the file is read
Definition: mpsinput.h:77
bool m_is_new_format
new MPS format?
Definition: mpsinput.h:105
int m_lineno
line number
Definition: mpsinput.h:79
const char * m_f0
first field in a line
Definition: mpsinput.h:87
void insertName(const char *name, bool second=false)
Inserts name as field 1 and shifts all other fields up.
Definition: mpsinput.cpp:267
bool readLine()
reads an MPS format data line and parse the fields.
Definition: mpsinput.cpp:67
const char * m_f2
third field in a line
Definition: mpsinput.h:91
const char * m_f3
fourth field in a line
Definition: mpsinput.h:93
const char * m_f4
fifth field in a line
Definition: mpsinput.h:95
static void debug(const T *, Args &&... args)
Definition: spxout.h:203
#define BLANK
Definition: mpsinput.cpp:38
#define PATCH_CHAR
Definition: mpsinput.cpp:37
Read MPS format files.
Everything should be within this namespace.
static void patch_field(char *buf, int beg, int end)
change all blanks inside a field to PATCH_CHAR.
Definition: mpsinput.cpp:53
static void clear_from(char *buf, int pos)
fill the line from pos up to column 80 with blanks.
Definition: mpsinput.cpp:44
Debugging, floating point type and parameter definitions.
Wrapper for different output streams and verbosity levels.