casadi_misc.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "mx.hpp"
27 
28 #include "casadi_misc.hpp"
29 #include "global_options.hpp"
30 #include "filesystem_impl.hpp"
31 
32 #include "casadi_os.hpp"
33 #include <sstream>
34 #ifdef HAVE_MKSTEMPS
35 #define CASADI_NEED_UNISTD
36 #else // HAVE_MKSTEMPS
37 #ifdef HAVE_SIMPLE_MKSTEMPS
38 #ifdef _WIN32
39 #include <io.h>
40 #include <share.h>
41 #else
42 #define CASADI_NEED_UNISTD
43 #endif
44 #include <random>
45 #include <chrono>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <errno.h>
49 #endif // HAVE_SIMPLE_MKSTEMPS
50 #endif // HAVE_MKSTEMPS
51 
52 #ifdef CASADI_NEED_UNISTD
53 #include <unistd.h>
54 #endif
55 
56 #undef CASADI_NEED_UNISTD
57 
58 namespace casadi {
59 
60  int to_int(casadi_int rhs) {
61  casadi_assert(rhs<=std::numeric_limits<int>::max(), "Integer overflow detected.");
62  casadi_assert(rhs>=std::numeric_limits<int>::min(), "Integer overflow detected.");
63  return rhs;
64  }
65 
66  std::vector<int> to_int(const std::vector<casadi_int>& rhs) {
67  std::vector<int> ret;
68  ret.reserve(rhs.size());
69  for (casadi_int e : rhs) ret.push_back(to_int(e));
70  return ret;
71  }
72 
73  std::vector< std::vector<int> > to_int(
74  const std::vector< std::vector<casadi_int> >& rhs) {
75  std::vector< std::vector<int> > ret;
76  ret.reserve(rhs.size());
77  for (const std::vector<casadi_int>& e : rhs) ret.push_back(to_int(e));
78  return ret;
79  }
80 
81  bool all(const std::vector<bool>& v) {
82  for (auto && e : v) {
83  if (!e) return false;
84  }
85  return true;
86  }
87 
88  bool any(const std::vector<bool>& v) {
89  for (auto && e : v) {
90  if (e) return true;
91  }
92  return false;
93  }
94 
95  bool is_range(const std::vector<casadi_int>& v,
96  casadi_int start, casadi_int stop, casadi_int step) {
97  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
98  if (v.size()!=nret) return false;
99  casadi_int ind = start;
100  for (casadi_int e : v) {
101  if (e!=ind) return false;
102  ind += step;
103  }
104  return true;
105  }
106 
107 
108  std::vector<casadi_int> range(casadi_int start, casadi_int stop,
109  casadi_int step, casadi_int len) {
110  start = std::min(start, len);
111  stop = std::min(stop, len);
112  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
113  std::vector<casadi_int> ret(nret);
114  casadi_int ind = start;
115  for (std::vector<casadi_int>::iterator it=ret.begin(); it!=ret.end(); ++it) {
116  *it = ind;
117  ind += step;
118  }
119  return ret;
120  }
121 
122  bool is_equally_spaced(const std::vector<double>& v) {
123  // Quick return if 2 or less entries
124  if (v.size()<=2) return true;
125  // Permitted error margin
126  // NOTE(@jaeandersson) 1e-14 good idea?
127  double margin = (v.back()-v.front())*1e-14;
128  // Make sure spacing is consistent throughout
129  double spacing = v[1]-v[0];
130  for (size_t i=2; i<v.size(); ++i) {
131  if (fabs(v[i]-v[i-1]-spacing)>margin) return false;
132  }
133  // Equal if reached this point
134  return true;
135  }
136 
137  std::vector<casadi_int> range(casadi_int stop) {
138  return range(0, stop);
139  }
140 
141  std::vector<casadi_int> complement(const std::vector<casadi_int> &v, casadi_int size) {
142  casadi_assert(in_range(v, size),
143  "complement: out of bounds. Some elements in v fall out of [0, size[");
144  std::vector<casadi_int> lookup(size, 0);
145  std::vector<casadi_int> ret;
146 
147  for (casadi_int i=0;i<v.size();i++) {
148  lookup[v[i]] = 1;
149  }
150 
151  for (casadi_int i=0;i<size;i++) {
152  if (lookup[i]==0) ret.push_back(i);
153  }
154 
155  return ret;
156 
157  }
158 
159  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v, casadi_int size) {
160  casadi_assert(in_range(v, size),
161  "lookupvector: out of bounds. Some elements in v fall out of [0, size[");
162  std::vector<casadi_int> lookup(size, -1);
163 
164  for (casadi_int i=0;i<v.size();i++) {
165  lookup[v[i]] = i;
166  }
167  return lookup;
168  }
169 
170  std::vector<casadi_int> lookupvector(const std::vector<casadi_int> &v) {
171  casadi_assert_dev(!has_negative(v));
172  return lookupvector(v, (*std::max_element(v.begin(), v.end()))+1);
173  }
174 
175  bool is_permutation(const std::vector<casadi_int> &order) {
176  std::set<casadi_int> order_set(order.begin(), order.end());
177  return (order_set.size()==order.size()) &&
178  (*order_set.begin()==0) &&
179  (*order_set.rbegin()==order.size()-1);
180  }
181 
182  std::vector<casadi_int> invert_permutation(const std::vector<casadi_int> &a) {
183  casadi_assert(is_permutation(a), "Not a permutation");
184  std::vector<casadi_int> ret(a.size());
185  for (casadi_int i=0;i<a.size();++i) {
186  ret[a[i]] = i;
187  }
188  return ret;
189  }
190 
191  bvec_t* get_bvec_t(std::vector<double>& v) {
192  if (v.empty()) {
193  return nullptr;
194  } else {
195  return reinterpret_cast<bvec_t*>(&v.front());
196  }
197  }
198 
200  const bvec_t* get_bvec_t(const std::vector<double>& v) {
201  if (v.empty()) {
202  return nullptr;
203  } else {
204  return reinterpret_cast<const bvec_t*>(&v.front());
205  }
206  }
207 
208  std::string join(const std::vector<std::string>& l, const std::string& delim) {
209  std::stringstream ss;
210  for (casadi_int i=0;i<l.size();++i) {
211  if (i>0) ss << delim;
212  ss << l[i];
213  }
214  return ss.str();
215  }
216 
217  bool startswith(const std::string& s, const std::string& p) {
218  if (p.size()>s.size()) return false;
219  for (casadi_int i=0;i<p.size();++i) {
220  if (s[i]!=p[i]) return false;
221  }
222  return true;
223  }
224 
225  CASADI_EXPORT std::string replace(const std::string& s,
226  const std::string& p, const std::string& r) {
227  std::string ret = s;
228  std::string::size_type n = 0;
229  while ((n = ret.find(p, n)) != std::string::npos) {
230  ret.replace(n, p.size(), r);
231  n += r.size();
232  }
233  return ret;
234  }
235 
236  bool version_gt(const std::string& version_left, const std::string& version_right) {
237  std::vector<casadi_int> left_parts, right_parts;
238 
239  // Parse left version
240  std::stringstream left_stream(version_left);
241  std::string part;
242  while (std::getline(left_stream, part, '.')) {
243  left_parts.push_back(std::stoi(part));
244  }
245 
246  // Parse right version
247  std::stringstream right_stream(version_right);
248  while (std::getline(right_stream, part, '.')) {
249  right_parts.push_back(std::stoi(part));
250  }
251 
252  // Compare component by component
253  casadi_int min_size = std::min(left_parts.size(), right_parts.size());
254  for (casadi_int i = 0; i < min_size; ++i) {
255  if (left_parts[i] > right_parts[i]) return true;
256  if (left_parts[i] < right_parts[i]) return false;
257  }
258 
259  // If all components are equal, the longer version is greater
260  return left_parts.size() > right_parts.size();
261  }
262 
263  bool version_ge(const std::string& version_left, const std::string& version_right) {
264  std::vector<casadi_int> left_parts, right_parts;
265 
266  // Parse left version
267  std::stringstream left_stream(version_left);
268  std::string part;
269  while (std::getline(left_stream, part, '.')) {
270  left_parts.push_back(std::stoi(part));
271  }
272 
273  // Parse right version
274  std::stringstream right_stream(version_right);
275  while (std::getline(right_stream, part, '.')) {
276  right_parts.push_back(std::stoi(part));
277  }
278 
279  // Compare component by component
280  casadi_int min_size = std::min(left_parts.size(), right_parts.size());
281  for (casadi_int i = 0; i < min_size; ++i) {
282  if (left_parts[i] > right_parts[i]) return true;
283  if (left_parts[i] < right_parts[i]) return false;
284  }
285 
286  // If all components are equal, the longer version is greater or equal
287  return left_parts.size() >= right_parts.size();
288  }
289 
290  bool version_lt(const std::string& version_left, const std::string& version_right) {
291  return version_gt(version_right, version_left);
292  }
293 
294  bool version_le(const std::string& version_left, const std::string& version_right) {
295  return version_ge(version_right, version_left);
296  }
297 
298 #ifdef HAVE_SIMPLE_MKSTEMPS
299 int simple_mkstemps_fd(const std::string& prefix, const std::string& suffix, std::string &result) {
300  // Characters available for inventing filenames
301  std::string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
302  int char_size = static_cast<int>(chars.size());
303 
304  // How many tries do we allow?
305  casadi_int max_tries = std::numeric_limits<int>::max();
306 
307  // How long should the ID be to cover all tries?
308  double max_tries_d = static_cast<double>(max_tries);
309  double char_size_d = static_cast<double>(char_size);
310  int id_size = lround(ceil(log(max_tries_d)/log(char_size_d)));
311 
312  // Random number generator
313  std::default_random_engine rng(std::chrono::system_clock::now().time_since_epoch().count());
314  std::uniform_int_distribution<> r(0, char_size-1);
315 
316  for (casadi_int i=0;i<max_tries;++i) {
317  result = prefix;
318  for (casadi_int j=0;j<id_size;++j) {
319  result += chars.at(r(rng));
320  }
321  result += suffix;
322 
323 #ifdef _WIN32
324  int fd = _sopen(result.c_str(),
325  _O_BINARY | _O_CREAT | _O_EXCL | _O_RDWR, _SH_DENYNO, _S_IREAD | _S_IWRITE);
326  // Could add _O_TEMPORARY, but then no possiblity of !cleanup_
327 #else
328  int fd = open(result.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
329 #endif
330  if (fd != -1) return fd;
331  if (fd == -1 && errno != EEXIST) return -1;
332  }
333  return 0;
334  }
335 std::string simple_mkstemps(const std::string& prefix, const std::string& suffix) {
336  std::string ret;
337  int fd = simple_mkstemps_fd(prefix, suffix, ret);
338  if (fd==-1) {
339  casadi_error("Failed to create temporary file: '" + ret + ". '"
340  + (Filesystem::is_enabled() ? "" : "Does the directory exits? "
341  "Note that CasADi needs to be compiled with WITH_GHC_FILESYSTEM=ON "
342  "for directories to be automatically created."));
343  } else {
344 #ifdef _WIN32
345  _close(fd);
346 #else
347  close(fd);
348 #endif
349  }
350  return ret;
351 }
352 #endif // HAVE_SIMPLE_MKSTEMPS
353 
354  std::string temporary_file(const std::string& prefix,
355  const std::string& suffix,
356  const std::string& directory) {
357 
358  std::string temp_dir = Filesystem::ensure_trailing_slash(directory);
359  if (temp_dir.empty()) temp_dir = GlobalOptions::getTempWorkDir();
360 
361  if (Filesystem::is_enabled()) {
362  casadi_assert(Filesystem::ensure_directory_exists(temp_dir),
363  "Unable to create the required directory for '" + temp_dir + "'.");
364  }
365 
366  #ifdef HAVE_MKSTEMPS
367  // Preferred solution
368  std::string ret = temp_dir + prefix + "XXXXXX" + suffix;
369  if (mkstemps(&ret[0], static_cast<int>(suffix.size())) == -1) {
370  casadi_error("Failed to create temporary file: '" + ret + "'. "
371  + (Filesystem::is_enabled() ? "" : "Does the directory exits? "
372  "Note that CasADi needs to be compiled with WITH_GHC_FILESYSTEM=ON "
373  "for directories to be automatically created."));
374  }
375  return ret;
376  #else // HAVE_MKSTEMPS
377  #ifdef HAVE_SIMPLE_MKSTEMPS
378  return simple_mkstemps(temp_dir + prefix, suffix);
379  #else // HAVE_SIMPLE_MKSTEMPS
380  casadi_assert(temp_dir=="./", "tmpnam fallback not compatible with custom temporary directory");
381  // Fallback, may result in deprecation warnings
382  return std::string(tmpnam(nullptr)) + suffix;
383  #endif // HAVE_SIMPLE_MKSTEMPS
384  #endif // HAVE_MKSTEMPS
385  }
386 
387 
388  std::vector<bool> boolvec_not(const std::vector<bool> &v) {
389  std::vector<bool> ret(v.size());
390  std::transform(v.begin(), v.end(), ret.begin(),
391  [](bool v) -> bool { return !v; });
392  return ret;
393  }
394 
395  std::vector<bool> boolvec_and(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
396  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
397  std::vector<bool> ret(lhs.size());
398  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
399  [](bool a, bool b) -> bool { return a && b; });
400  return ret;
401  }
402 
403  std::vector<bool> boolvec_or(const std::vector<bool> &lhs, const std::vector<bool> &rhs) {
404  casadi_assert(lhs.size()==rhs.size(), "Size mismatch.");
405  std::vector<bool> ret(lhs.size());
406  std::transform(lhs.begin(), lhs.end(), rhs.begin(), ret.begin(),
407  [](bool a, bool b) -> bool { return a || b; });
408  return ret;
409  }
410 
411 
412  std::vector<casadi_int> boolvec_to_index(const std::vector<bool> &v) {
413  std::vector<casadi_int> ret;
414  for (casadi_int i=0;i<v.size();++i) {
415  if (v[i]) ret.push_back(i);
416  }
417  return ret;
418  }
419 
420  void normalized_setup(std::istream& stream) {
421  stream.imbue(std::locale("C"));
422  }
423 
424  void normalized_setup(std::ostream& stream) {
425  stream.imbue(std::locale("C"));
426  stream << std::scientific;
427  stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
428  }
429 
431  : stream_(os),
432  flags_(os.flags()),
433  precision_(os.precision()),
434  width_(os.width()),
435  locale_(os.getloc()) {}
436 
438  stream_.flags(flags_);
439  stream_.precision(precision_);
440  stream_.width(width_);
441  stream_.imbue(locale_);
442  }
443 
444  std::string str_bvec(bvec_t v) {
445  std::stringstream ss;
446  for (casadi_int i=0;i<sizeof(bvec_t)*8;++i) {
447  bool bit = v & (bvec_t(1) << i);
448  ss << (bit ? "1" : "0");
449  }
450  return ss.str();
451  }
452 
453  bvec_t bvec_or(const bvec_t* arg, casadi_int n) {
454  bvec_t acc = 0;
455  // vacuous truth
456  if (n==0) {
457  return~acc;
458  }
459  for (casadi_int i=0;i<n;++i) {
460  acc |= arg[i];
461  }
462  return acc;
463  }
464 
465 } // namespace casadi
static std::string ensure_trailing_slash(const std::string &path)
Definition: filesystem.cpp:155
static bool ensure_directory_exists(const std::string &path)
Definition: filesystem.cpp:105
static bool is_enabled()
Definition: filesystem.cpp:83
static std::string getTempWorkDir()
StreamStateGuard(std::ostream &os)
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
bvec_t * get_bvec_t(std::vector< double > &v)
bool is_equally_spaced(const std::vector< double > &v)
std::string join(const std::vector< std::string > &l, const std::string &delim)
std::vector< casadi_int > invert_permutation(const std::vector< casadi_int > &a)
inverse a permutation vector
unsigned long long bvec_t
bool version_le(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left <= version_right.
bool is_range(const std::vector< casadi_int > &v, casadi_int start, casadi_int stop, casadi_int step)
Check if a vector matches a range.
Definition: casadi_misc.cpp:95
bool startswith(const std::string &s, const std::string &p)
Checks if s starts with p.
int to_int(casadi_int rhs)
Definition: casadi_misc.cpp:60
bool has_negative(const std::vector< T > &v)
Check if the vector has negative entries.
bool version_gt(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left > version_right.
CASADI_EXPORT std::string replace(const std::string &s, const std::string &p, const std::string &r)
Replace all occurences of p with r in s.
std::string str_bvec(bvec_t v)
std::vector< bool > boolvec_or(const std::vector< bool > &lhs, const std::vector< bool > &rhs)
Or operation on boolean vector.
bool version_lt(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left < version_right.
std::vector< casadi_int > lookupvector(const std::vector< casadi_int > &v, casadi_int size)
Returns a vector for quickly looking up entries of supplied list.
std::vector< bool > boolvec_not(const std::vector< bool > &v)
Invert all entries.
std::vector< bool > boolvec_and(const std::vector< bool > &lhs, const std::vector< bool > &rhs)
And operation on boolean vector.
bool any(const std::vector< bool > &v)
Check if any arguments are true.
Definition: casadi_misc.cpp:88
void normalized_setup(std::istream &stream)
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:81
bool in_range(const std::vector< T > &v, casadi_int upper)
Check if for each element of v holds: v_i < upper.
bool is_permutation(const std::vector< casadi_int > &order)
Does the list represent a permutation?
std::vector< casadi_int > complement(const std::vector< casadi_int > &v, casadi_int size)
Returns the list of all i in [0, size[ not found in supplied list.
bool version_ge(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left >= version_right.
bvec_t bvec_or(const bvec_t *arg, casadi_int n)
Bit-wise or operation on bvec_t array.
std::string temporary_file(const std::string &prefix, const std::string &suffix, const std::string &directory)
std::vector< casadi_int > boolvec_to_index(const std::vector< bool > &v)