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-2024 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[12] | m_buf[13]
141 | m_buf[22] | m_buf[23]
142 | m_buf[36] | m_buf[37] | m_buf[38]
143 | m_buf[47] | m_buf[48]
144 | m_buf[61] | m_buf[62] | m_buf[63];
145
146 if(space == BLANK || len < 13)
147 {
148 /* Now we have space at the right positions.
149 * But are there also the non space where they
150 * should be ?
151 */
152 bool number = isdigit(m_buf[24]) || isdigit(m_buf[25])
153 || isdigit(m_buf[26]) || isdigit(m_buf[27])
154 || isdigit(m_buf[28]) || isdigit(m_buf[29])
155 || isdigit(m_buf[30]) || isdigit(m_buf[31])
156 || isdigit(m_buf[32]) || isdigit(m_buf[33])
157 || isdigit(m_buf[34]) || isdigit(m_buf[35]);
158
159 /* len < 13 is handle ROW lines with embedded spaces
160 * in the names correctly
161 */
162 if(number || len < 13)
163 {
164 /* Now we assume fixed format, so we patch possible embedded spaces.
165 */
166 patch_field(m_buf, 4, 12);
167 patch_field(m_buf, 14, 22);
168 patch_field(m_buf, 39, 47);
169 }
170 else
171 {
172 if(m_section == COLUMNS || m_section == RHS
173 || m_section == RANGES || m_section == BOUNDS)
174 m_is_new_format = true;
175 }
176 }
177 else
178 {
179 m_is_new_format = true;
180 }
181 }
182
183 s = &m_buf[1];
184
185 /* At this point it is not clear if we have a indicator field.
186 * If there is none (e.g. empty) f1 will be the first name field.
187 * If there is one, f2 will be the first name field.
188 *
189 * Initially comment marks '$' ar only allowed in the beginning
190 * of the 2nd and 3rd name field. We test all fields but the first.
191 * This makes no difference, since if the $ is at the start of a value
192 * field, the line will be errornous anyway.
193 */
194 do
195 {
196 if(nullptr == (m_f1 = strtok(s, " ")))
197 break;
198
199 if((nullptr == (m_f2 = strtok(nullptr, " "))) || (*m_f2 == '$'))
200 {
201 m_f2 = nullptr;
202 break;
203 }
204
205 if(!strcmp(m_f2, "'MARKER'"))
206 is_marker = true;
207
208 if((nullptr == (m_f3 = strtok(nullptr, " "))) || (*m_f3 == '$'))
209 {
210 m_f3 = nullptr;
211 break;
212 }
213
214 if(is_marker)
215 {
216 if(!strcmp(m_f3, "'INTORG'"))
217 m_is_integer = true;
218 else if(!strcmp(m_f3, "'INTEND'"))
219 m_is_integer = false;
220 else
221 break; // unknown marker
222 }
223
224 if(!strcmp(m_f3, "'MARKER'"))
225 is_marker = true;
226
227 if((nullptr == (m_f4 = strtok(nullptr, " "))) || (*m_f4 == '$'))
228 {
229 m_f4 = nullptr;
230 break;
231 }
232
233 if(is_marker)
234 {
235 if(!strcmp(m_f4, "'INTORG'"))
236 m_is_integer = true;
237 else if(!strcmp(m_f4, "'INTEND'"))
238 m_is_integer = false;
239 else
240 break; // unknown marker
241 }
242
243 if((nullptr == (m_f5 = strtok(nullptr, " "))) || (*m_f5 == '$'))
244 m_f5 = nullptr;
245 }
246 while(false);
247 }
248 while(is_marker);
249
250 SPxOut::debug(this, "DMPSIN02 -----------------------------------------------\n"
251 "DMPSIN03 f0={}\n"
252 "DMPSIN04 f1={}\n"
253 "DMPSIN05 f2={}\n"
254 "DMPSIN07 f4={}\n"
255 "DMPSIN06 f3={}\n"
256 "DMPSIN08 f5={}\n"
257 "DMPSIN09 -----------------------------------------------\n",
258 ((m_f0 == nullptr) ? "nil" : m_f0),
259 ((m_f1 == nullptr) ? "nil" : m_f1),
260 ((m_f2 == nullptr) ? "nil" : m_f2),
261 ((m_f3 == nullptr) ? "nil" : m_f3),
262 ((m_f4 == nullptr) ? "nil" : m_f4),
263 ((m_f5 == nullptr) ? "nil" : m_f5));
264 return true;
265}
266
267/// Insert \p name as field 1 and shift all other fields up.
268void MPSInput::insertName(const char* name, bool second)
269{
270 m_f5 = m_f4;
271 m_f4 = m_f3;
272 m_f3 = m_f2;
273
274 if(second)
275 m_f2 = name;
276 else
277 {
278 m_f2 = m_f1;
279 m_f1 = name;
280 }
281}
282
283} // 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:268
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.