1    	/*
2    	 .nl file support.
3    	
4    	 Copyright (C) 2013 AMPL Optimization Inc
5    	
6    	 Permission to use, copy, modify, and distribute this software and its
7    	 documentation for any purpose and without fee is hereby granted,
8    	 provided that the above copyright notice appear in all copies and that
9    	 both that the copyright notice and this permission notice and warranty
10   	 disclaimer appear in supporting documentation.
11   	
12   	 The author and AMPL Optimization Inc disclaim all warranties with
13   	 regard to this software, including all implied warranties of
14   	 merchantability and fitness.  In no event shall the author be liable
15   	 for any special, indirect or consequential damages or any damages
16   	 whatsoever resulting from loss of use, data or profits, whether in an
17   	 action of contract, negligence or other tortious action, arising out
18   	 of or in connection with the use or performance of this software.
19   	
20   	 Author: Victor Zverovich
21   	 */
22   	
23   	#include "mp/nl-reader.h"
24   	
25   	namespace {
26   	enum {
27   	  USE_VBTOL_OPTION = 1,
28   	  READ_VBTOL       = 3
29   	};
30   	}
31   	
32   	void mp::ReadError::init(fmt::CStringRef filename, int line, int column,
33   	                         fmt::CStringRef format_str, fmt::ArgList args) {
34   	  filename_ = filename.c_str();
35   	  line_ = line;
36   	  column_ = column;
37   	  fmt::MemoryWriter w;
38   	  w.write("{}:{}:{}: ", filename, line, column);
39   	  w.write(format_str, args);
40   	  SetMessage(w.c_str());
41   	}
42   	
43   	mp::arith::Kind mp::arith::GetKind() {
44   	  // Unlike ASL, we don't try detecting floating-point arithmetic at
45   	  // configuration time because it doesn't work with cross-compiling.
46   	  if (sizeof(double) != 2 * sizeof(uint32_t))
47   	    return UNKNOWN;
48   	  union {
49   	    double d;
50   	    uint32_t i[2];
51   	  } u;
52   	  u.i[0] = u.i[1] = 0;
53   	  u.d = 1e13;
54   	  if (u.i[1] == 0x42A2309C && u.i[0] == 0xE5400000)
55   	    return IEEE_LITTLE_ENDIAN;
56   	  if (u.i[0] == 0x42A2309C && u.i[1] == 0xE5400000)
57   	    return IEEE_BIG_ENDIAN;
58   	  return UNKNOWN;
59   	}
60   	
61   	fmt::Writer &mp::operator<<(fmt::Writer &w, const NLHeader &h) {
62   	  w << (h.format == NLHeader::TEXT ? 'g' : 'b') << h.num_ampl_options;
63   	  for (int i = 0; i < h.num_ampl_options; ++i)
64   	    w << ' ' << h.ampl_options[i];
65   	  if (h.ampl_options[USE_VBTOL_OPTION] == READ_VBTOL)
66   	    w << ' ' << h.ampl_vbtol;
67   	  w << '\n';
68   	  w.write(" {} {} {} {} {} {}\n",
69   	      h.num_vars, h.num_algebraic_cons, h.num_objs,
70   	      h.num_ranges, h.num_eqns, h.num_logical_cons);
71   	  w.write(" {} {} {} {} {} {}\n",
72   	      h.num_nl_cons, h.num_nl_objs,
73   	      h.num_compl_conds - h.num_nl_compl_conds,
74   	      h.num_nl_compl_conds, h.num_compl_dbl_ineqs,
75   	      h.num_compl_vars_with_nz_lb);
76   	  w.write(" {} {}\n", h.num_nl_net_cons, h.num_linear_net_cons);
77   	  w.write(" {} {} {}\n",
78   	      h.num_nl_vars_in_cons, h.num_nl_vars_in_objs, h.num_nl_vars_in_both);
79   	  w.write(" {} {} {} {}\n",
80   	      h.num_linear_net_vars, h.num_funcs,
81   	          h.format == NLHeader::TEXT ? 0 : h.arith_kind, h.flags);
82   	  w.write(" {} {} {} {} {}\n",
83   	      h.num_linear_binary_vars, h.num_linear_integer_vars,
84   	      h.num_nl_integer_vars_in_both, h.num_nl_integer_vars_in_cons,
85   	      h.num_nl_integer_vars_in_objs);
86   	  w.write(" {} {}\n", h.num_con_nonzeros, h.num_obj_nonzeros);
87   	  w.write(" {} {}\n", h.max_con_name_len, h.max_var_name_len);
88   	  w.write(" {} {} {} {} {}\n",
89   	      h.num_common_exprs_in_both, h.num_common_exprs_in_cons,
90   	      h.num_common_exprs_in_objs, h.num_common_exprs_in_single_cons,
91   	      h.num_common_exprs_in_single_objs);
92   	  return w;
93   	}
94   	
95   	mp::internal::ReaderBase::ReaderBase(NLStringRef data, fmt::CStringRef name)
96   	: ptr_(data.c_str()), start_(ptr_), end_(ptr_ + data.size()),
97   	  token_(ptr_), name_(name.c_str()) {}
98   	
99   	template <typename Locale>
100  	mp::internal::TextReader<Locale>::TextReader(
101  	    NLStringRef data, fmt::CStringRef name)
102  	: ReaderBase(data, name), line_start_(ptr_), line_(1) {}
103  	
104  	template <typename Locale>
105  	void mp::internal::TextReader<Locale>::DoReportError(
106  	    const char *loc, fmt::CStringRef format_str, const fmt::ArgList &args) {
107  	  int line = line_;
108  	  const char *line_start = line_start_;
109  	  if (loc < line_start) {
110  	    --line;
111  	    // Find the beginning of the previous line.
112  	    line_start = loc;
113  	    if (*line_start == '\n')
114  	      --line_start;
115  	    while (*line_start != '\n' && line_start != start_)
116  	      --line_start;
117  	    if (*line_start == '\n')
118  	      ++line_start;
119  	  }
120  	  int column = static_cast<int>(loc - line_start + 1);
121  	  throw ReadError(name_, line, column, format_str, args);
122  	}
123  	
124  	template <typename Locale>
125  	bool mp::internal::TextReader<Locale>::ReadOptionalDouble(double &value) {
126  	  SkipSpace();
127  	  if (*ptr_ == '\n')
128  	    return false;
129  	  char *end = 0;
130  	  value = std::strtod(ptr_, &end);
131  	  bool has_value = ptr_ != end;
132  	  ptr_ = end;
133  	  return has_value;
134  	}
135  	
136  	template <typename Locale>
137  	fmt::StringRef mp::internal::TextReader<Locale>::ReadString() {
138  	  int length = ReadUInt();
139  	  if (*ptr_ != ':')
140  	    DoReportError(ptr_, "expected ':'");
141  	  ++ptr_;
142  	  const char *start = ptr_;
143  	  for (int i = 0; i < length; ++i, ++ptr_) {
144  	    char c = *ptr_;
145  	    if (c == '\n') {
146  	      line_start_ = ptr_  + 1;
147  	      ++line_;
148  	    } else if (!c && ptr_ == end_) {
149  	      DoReportError(ptr_, "unexpected end of file in string");
150  	    }
151  	  }
152  	  if (*ptr_ != '\n')
153  	    DoReportError(ptr_, "expected newline");
154  	  ++line_;
155  	  line_start_ = ++ptr_;
156  	  return fmt::StringRef(length != 0 ? start : 0, length);
157  	}
158  	
159  	template <typename Locale>
160  	fmt::StringRef mp::internal::TextReader<Locale>::ReadName() {
161  	  SkipSpace();
162  	  const char *start = ptr_;
163  	  if (*ptr_ == '\n' || !*ptr_)
164  	    ReportError("expected name");
165  	  do ++ptr_;
166  	  while (!std::isspace(*ptr_) && *ptr_);
167  	  return fmt::StringRef(start, ptr_ - start);
168  	}
169  	
170  	template <typename Locale>
171  	void mp::internal::TextReader<Locale>::ReadHeader(NLHeader &header) {
172  	  // Read the format (text or binary).
173  	  switch (ReadChar()) {
174  	  case 'g':
175  	    break;
176  	  case 'b':
177  	    header.format = NLHeader::BINARY;
178  	    break;
179  	  default:
180  	    ReportError("expected format specifier");
181  	    break;
182  	  }
183  	
184  	  // Read options.
185  	  ReadOptionalUInt(header.num_ampl_options);
186  	  if (header.num_ampl_options > MAX_AMPL_OPTIONS)
187  	    ReportError("too many options");
188  	  for (int i = 0; i < header.num_ampl_options; ++i) {
189  	    if (!ReadOptionalInt(header.ampl_options[i]))
190  	      break;
191  	  }
192  	  if (header.ampl_options[USE_VBTOL_OPTION] == READ_VBTOL)
193  	    ReadOptionalDouble(header.ampl_vbtol);
194  	  ReadTillEndOfLine();
195  	
196  	  // Read problem dimensions.
197  	  header.num_vars = ReadUInt();
198  	  header.num_algebraic_cons = ReadUInt();
199  	  header.num_objs = ReadUInt();
200  	  header.num_eqns = -1;
201  	  if (ReadOptionalUInt(header.num_ranges) &&
202  	      ReadOptionalUInt(header.num_eqns)) {
203  	      ReadOptionalUInt(header.num_logical_cons);
204  	  }
205  	  ReadTillEndOfLine();
206  	
207  	  // Read the nonlinear and complementarity information.
208  	  header.num_nl_cons = ReadUInt();
209  	  header.num_nl_objs = ReadUInt();
210  	  bool all_compl =
211  	      ReadOptionalUInt(header.num_compl_conds) &&
212  	      ReadOptionalUInt(header.num_nl_compl_conds) &&
213  	      ReadOptionalUInt(header.num_compl_dbl_ineqs) &&
214  	      ReadOptionalUInt(header.num_compl_vars_with_nz_lb);
215  	  header.num_compl_conds += header.num_nl_compl_conds;
216  	  if (header.num_compl_conds > 0 && !all_compl)
217  	    header.num_compl_dbl_ineqs = -1;
218  	  ReadTillEndOfLine();
219  	
220  	  // Read the information about network constraints.
221  	  header.num_nl_net_cons = ReadUInt();
222  	  header.num_linear_net_cons = ReadUInt();
223  	  ReadTillEndOfLine();
224  	
225  	  // Read the information about nonlinear variables.
226  	  header.num_nl_vars_in_cons = ReadUInt();
227  	  header.num_nl_vars_in_objs = ReadUInt();
228  	  header.num_nl_vars_in_both = -1;
229  	  ReadOptionalUInt(header.num_nl_vars_in_both);
230  	  ReadTillEndOfLine();
231  	
232  	  header.num_linear_net_vars = ReadUInt();
233  	  header.num_funcs = ReadUInt();
234  	  int arith_kind = 0;
235  	  if (ReadOptionalUInt(arith_kind)) {
236  	    if (arith_kind > arith::LAST)
237  	      ReportError("unknown floating-point arithmetic kind");
238  	    header.arith_kind = static_cast<arith::Kind>(arith_kind);
239  	    ReadOptionalUInt(header.flags);
240  	  }
241  	  ReadTillEndOfLine();
242  	
243  	  // Read the information about discrete variables.
244  	  header.num_linear_binary_vars = ReadUInt();
245  	  header.num_linear_integer_vars = ReadUInt();
246  	  if (header.num_nl_vars_in_both >= 0) {  // ampl versions >= 19930630
247  	    header.num_nl_integer_vars_in_both = ReadUInt();
248  	    header.num_nl_integer_vars_in_cons = ReadUInt();
249  	    header.num_nl_integer_vars_in_objs = ReadUInt();
250  	  }
251  	  ReadTillEndOfLine();
252  	
253  	  // Read the information about nonzeros.
254  	  header.num_con_nonzeros = ReadUInt<std::size_t>();
255  	  header.num_obj_nonzeros = ReadUInt<std::size_t>();
256  	  ReadTillEndOfLine();
257  	
258  	  // Read the information about names.
259  	  header.max_con_name_len = ReadUInt();
260  	  header.max_var_name_len = ReadUInt();
261  	  ReadTillEndOfLine();
262  	
263  	  // Read the information about common expressions checking for overflow
264  	  // as the variable indices go from 0 to num_vars + num_common_exprs.
265  	  int max_vars = header.num_vars;
266  	  header.num_common_exprs_in_both = ReadUInt(max_vars);
267  	  header.num_common_exprs_in_cons = ReadUInt(max_vars);
268  	  header.num_common_exprs_in_objs = ReadUInt(max_vars);
269  	  header.num_common_exprs_in_single_cons = ReadUInt(max_vars);
270  	  header.num_common_exprs_in_single_objs = ReadUInt(max_vars);
271  	  ReadTillEndOfLine();
272  	}
273  	
274  	void mp::internal::BinaryReaderBase::ReportError(
275  	    fmt::CStringRef format_str, const fmt::ArgList &args) {
276  	  fmt::MemoryWriter w;
277  	  std::size_t offset = token_ - start_;
278  	  w.write("{}:offset {}: ", name_, offset);
279  	  w.write(format_str, args);
280  	  throw BinaryReadError(name_, offset, w.c_str());
281  	}
282  	
283  	template <typename File>
284  	void mp::internal::NLFileReader<File>::Open(fmt::CStringRef filename) {
285  	  file_ = File(filename, fmt::File::RDONLY);
286  	  size_ = ConvertFileToMmapSize(file_.size(), filename);
287  	  // Round size up to a multiple of page_size. The remainded of the last
288  	  // partial page is zero-filled both on POSIX and Windows so the resulting
289  	  // memory buffer is zero terminated.
290  	  std::size_t page_size = fmt::getpagesize();
291  	  std::size_t remainder = size_ % page_size;
292  	  rounded_size_ = remainder != 0 ? (size_ + page_size - remainder) : size_;
293  	}
294  	
295  	template <typename File>
296  	void mp::internal::NLFileReader<File>::Read(
297  	    fmt::internal::MemoryBuffer<char, 1> &array) {
298  	  array.resize(size_ + 1);
299  	  std::size_t offset = 0;
300  	  while (offset < size_)
301  	    offset += file_.read(&array[offset], size_ - offset);
302  	  array[size_] = 0;
303  	}
304  	
305  	template class mp::internal::TextReader<>;
306  	template class mp::internal::NLFileReader<>;
307