source: trunk/yat/utility/utility.h @ 3177

Last change on this file since 3177 was 3177, checked in by Peter, 9 years ago

Change behaviour if convert<T> and is<T> so they accept "-nan" (case insensitively) as an argument if T is a signed type. closes #783

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.6 KB
Line 
1#ifndef _theplu_yat_utility_utility_
2#define _theplu_yat_utility_utility_
3
4// $Id: utility.h 3177 2014-03-15 13:50:51Z peter $
5
6/*
7  Copyright (C) 2005 Jari Häkkinen, Peter Johansson, Markus Ringnér
8  Copyright (C) 2006 Jari Häkkinen
9  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson
10  Copyright (C) 2009, 2010, 2011, 2012, 2013 Peter Johansson
11
12  This file is part of the yat library, http://dev.thep.lu.se/yat
13
14  The yat library is free software; you can redistribute it and/or
15  modify it under the terms of the GNU General Public License as
16  published by the Free Software Foundation; either version 3 of the
17  License, or (at your option) any later version.
18
19  The yat library is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  General Public License for more details.
23
24  You should have received a copy of the GNU General Public License
25  along with yat. If not, see <http://www.gnu.org/licenses/>.
26*/
27
28///
29/// \file yat/utility/utility.h
30///
31/// @brief Some useful functions are placed here
32///
33
34#include "deprecate.h"
35#include "Exception.h"
36#include "yat_assert.h"
37
38#include <gsl/gsl_math.h>
39
40#include <algorithm>
41#include <cctype>
42#include <cmath>
43#include <functional>
44#include <limits>
45#include <locale>
46#include <istream>
47#include <string>
48#include <stdexcept>
49#include <sstream>
50#include <utility>
51#include <vector>
52
53namespace theplu {
54namespace yat {
55namespace utility {
56
57  /**
58     Same as posix C function with same name but works on \c
59     std::string rather than \c char*.
60
61     \see http://linux.die.net/man/3/basename
62
63     \return everything after last '/'
64
65     \since New in yat 0.10
66   */
67  std::string basename(const std::string& fn);
68
69  /**
70     For each element in resulting range assign it to 0.0 if
71     corresponding element in input range is NaN else assign it to
72     1.0.
73
74     \return true if there is at least one NaN in input range
75     [first, last).
76
77     \since New in yat 0.5
78  */
79  template<typename InputIterator, typename OutputIterator>
80  bool binary_weight(InputIterator first, InputIterator last,
81                     OutputIterator result);
82
83  /**
84     Same as C function \c chdir but throws on failure (instead of
85     retrning non-zero).
86
87     \throw runtime_error if underlying chdir returns non-zero
88
89     \see http://linux.die.net/man/3/chdir
90
91     \since New in yat 0.10
92   */
93  void chdir(const std::string& dir);
94
95  /**
96     same as C function \c chmod but throws on failure (instead of
97     returning non-zero).
98
99     \see http://linux.die.net/man/3/chmod
100
101     \since New in yat 0.10
102   */
103  void chmod(const std::string& filename, mode_t mode);
104
105  /**
106     \brief convert T to a string
107
108     T is supposed to be a numerical type.
109
110     \since new in yat 0.8
111   */
112  template<typename T>
113  std::string convert(T input);
114
115  /**
116     \brief convert string to (numerical) type
117
118     \throw runtime_error if conversion fails
119   */
120  template<typename T>
121  T convert(const std::string& s);
122
123  /**
124     @brief Copy file \a source to \a target.
125
126     @throw runtime_error if read error of \a source or write error
127     for \a target is encountered.
128  */
129  void copy_file(const std::string& source, const std::string& target);
130
131  /**
132     Same as posix C function with same name but works on \c
133     std::string rather than \c char*.
134
135     \see http://linux.die.net/man/3/dirname
136
137     \return everything prior last '/'.
138
139     \since New in yat 0.10
140   */
141  std::string dirname(const std::string& fn);
142
143  /**
144     \return true if \a str matches \a pattern
145
146     \see http://linux.die.net/man/3/fnmatch
147
148     \throw runtime_error if returned value from underlying fnmatch is
149     neither zero nor \c FNM_NOMATCH.
150
151     \since New in yat 0.10
152  */
153  bool fnmatch(const std::string& pattern, const std::string& str,
154               int flags=0);
155
156  /**
157     \brief check if string is convertible to (numerical) type \c T
158
159     \since New in yat 0.5
160   */
161  template<typename T>
162  bool is(const std::string& s);
163
164  ///
165  /// @return true if string is a double
166  ///
167  /// \deprecated Provided for backward compatibility with the 0.4
168  /// API. Use is<double>(const std::string&)
169  ///
170  bool is_double(const std::string&) YAT_DEPRECATE;
171
172  /**
173     This function takes the first word (separated by whitespace) in
174     \a s, replaces all upper case with lower case, and compares it
175     with \a other.
176
177     \return true if processed \a s is equal to \a other. It returns
178     false otherwise or if \a s contains more than one word.
179  */
180  bool is_equal(std::string s, std::string other);
181
182  ///
183  /// @return true if string is a float
184  ///
185  /// \deprecated Provided for backward compatibility with the 0.4
186  /// API. Use is<float>(const std::string&)
187  ///
188  bool is_float(const std::string&) YAT_DEPRECATE;
189
190  ///
191  /// @return true if string is an int
192  ///
193  /// \deprecated Provided for backward compatibility with the 0.4
194  /// API. Use is<int>(const std::string&)
195  ///
196  bool is_int(const std::string&) YAT_DEPRECATE;
197
198  ///
199  /// @return true if string is "nan" (case-insensitive)
200  ///
201  bool is_nan(const std::string& s);
202
203  /**
204     The std::istream will be interpreted as outlined here:
205
206     Lines are separated by character \a line_sep and rows are
207     separated by character \a sep.
208     
209     The first line is read into a stringstream, which is used to
210     load the first vector (vec[0]) with elements using
211     load(stringstream, vec[0], sep).
212     
213     Therefore, column elements separation has two modes depending
214     on the value of \a sep.
215     
216     - If \a sep is the default '\\0' value then column elements are
217     separated with white space characters except the new line
218     character. Multiple sequential white space characters are treated
219     as one separator.
220     
221     - Setting \a sep to something else than the default value will
222     change the behaviour to use the \a sep character as the separator
223     between column elements. Multiple sequential \a sep characters
224     will be treated as separating elements with missing values.
225
226     If \a rectangle is true, rows must contain same number of
227     elements or function will throw.
228
229     If \a ignore_empty is true empty lines are ignored.
230
231     \see load(std::istream&, std::vector<T>&, char sep='\\0')
232
233     \note Requirement on T: utility::convert<T> must be supported
234     (from yat 0.7 T=string is also supported)
235
236     \since New in yat 0.6
237   */
238  template<typename T>
239  void load(std::istream& is, std::vector<std::vector<T> >& vec, char sep='\0', 
240            char line_sep='\n', bool ignore_empty=false, bool rectangle=true);
241
242  /**
243     \brief Fill a vector<T> with elements from istream
244
245     Element separation has two modes depending on the value of \a
246     sep.
247     
248     - If \a sep is the default '\\0' value then elements are
249     separated with white space characters. Multiple sequential white
250     space characters are treated as one separator.
251     
252     - Setting \a sep to something else than the default value will
253     change the behaviour to use the \a sep character as the
254     separator between column elements. Multiple sequential \a sep
255     characters will be treated as separating elements with missing
256     values. Missing values are set to std::numeric_limits<T>::quiet_NaN
257     
258     \note Requirement on T: utility::convert<T> must be supported
259     (from yat 0.7 T=string is also supported)
260
261     \since New in yat 0.6
262   */
263  template<typename T>
264  void load(std::istream& is, std::vector<T>& vec, char sep='\0');
265
266  /**
267     \return base-2 logarithm of x
268
269     \since New in yat 0.10
270   */
271  // c++11 provides std::log2 so perhaps we should call that one if
272  // availalable (but a bit tricky since this is a public header)
273  template<typename T>
274  T log2(T x) { return std::log(x)/M_LN2; }
275
276// private namespace
277namespace detail {
278
279  /**
280     \brief convert s to t
281
282     used in function is<T> and convert<T>
283
284     \return true if conversion was successful
285
286     \internal
287   */
288  template<typename T>
289  bool convert(const std::string& s, T& t);
290
291  /**
292     Functor used in load function
293   */
294  template<typename T>
295  struct VectorPusher
296  {
297    /**
298       convert element to T and push on vec's back
299
300       \internal
301     */
302    void operator()(const std::string& element, std::vector<T>& vec)
303    { 
304      if (!element.size())
305        vec.push_back(std::numeric_limits<T>::quiet_NaN());
306      else {
307        vec.push_back(theplu::yat::utility::convert<T>(element));
308      }
309    }
310  };
311
312  /**
313     specialization for string
314
315     \internal
316   */
317  template<>
318  struct VectorPusher<std::string>
319  {
320    /**
321       push element on vec's back
322     */
323    void operator()(const std::string& element, std::vector<std::string>& vec)
324    { 
325      vec.push_back(element);
326    }
327  };
328
329} // end of namespace detail
330
331
332  /**
333     \brief create a directory \a dir
334
335     \see http://linux.die.net/man/3/mkdir
336
337     \throw runtime_error if creation failed
338
339     \since New in yat 0.10
340   */
341  void mkdir(const std::string& dir, mode_t mode=0777);
342
343  /**
344     Similar to mkdir(const std::string&, mode_t).
345
346     No error if \a dir already exist. Make parent directories as needed.
347
348     \since New in yat 0.10
349   */
350  void mkdir_p(const std::string& dir, mode_t mode=0777);
351
352  /**
353     same as C function remove but throws errno_error at failure
354
355     \see http://linux.die.net/man/3/remove
356
357     \since New in yat 0.10
358   */
359  void remove(const std::string& fn);
360
361  /**
362     same as C function with same name but throws errno_error if error
363     is encountered
364
365     \see http://linux.die.net/man/3/rename
366
367     \since New in yat 0.10
368   */
369  void rename(const std::string& from, const std::string to);
370
371  /**
372     In \a full_str replace every sub-string \a old_str with \a
373     new_str;
374
375     \since New in yat 0.10
376   */
377  void replace(std::string& full_str, std::string old_str, std::string new_str);
378
379  // template implementations
380
381  template<typename InputIterator, typename OutputIterator>
382  bool binary_weight(InputIterator first, InputIterator last, 
383                     OutputIterator result)
384  {
385    bool nan=false;
386    while (first!=last) {
387      if (std::isnan(*first)) {
388        *result=0;
389        nan=true;
390      }
391      else
392        *result = 1.0;
393      ++first;
394      ++result;
395    }
396    return nan;
397  }
398
399
400  // template implementations
401  template<typename T>
402  std::string convert(T input)
403  {
404    std::ostringstream ss;
405    ss << input;
406    return ss.str();
407  }
408
409
410  template<typename T>
411  T convert(const std::string& s)
412  {
413    T result;
414    if (!detail::convert(s, result))
415      throw runtime_error(std::string("yat::utility::convert(\"")+s+
416                          std::string("\")"));
417    return result;
418  }
419
420
421  template<typename T>
422  bool is(const std::string& s)
423  {
424    T tmp;
425    return detail::convert(s, tmp);
426  }
427
428
429  template<typename T>
430  void load(std::istream& is, std::vector<std::vector<T> >& matrix, 
431            char sep, char line_sep, bool ignore_empty, 
432            bool rectangle)
433  {
434    size_t nof_columns=0;
435    std::string line;
436    while(getline(is, line, line_sep)){
437      if (line.empty() && ignore_empty)
438        continue;
439      matrix.push_back(std::vector<T>());
440      std::vector<T>& v=matrix.back();
441      v.reserve(nof_columns);
442      std::stringstream ss(line);
443      load(ss, v, sep);
444      // add NaN for final separator (or empty string if T=std::string)
445      detail::VectorPusher<T> pusher;
446      if(sep!='\0' && !line.empty() && line[line.size()-1]==sep) 
447        pusher("", v);
448     
449      if (rectangle && nof_columns && v.size()!=nof_columns) {
450        std::ostringstream s;
451        s << "load stream error: "
452          << "line " << matrix.size() << " has " << v.size()
453          << " columns; expected " << nof_columns << " columns.";
454        throw utility::IO_error(s.str());
455      }       
456      nof_columns = std::max(nof_columns, v.size());
457    }
458
459    // manipulate the state of the stream to be good
460    is.clear(std::ios::goodbit);
461  }
462
463  template<typename T>
464  void load(std::istream& is, std::vector<T>& vec, char sep)
465  {
466    detail::VectorPusher<T> pusher;
467    std::string element;
468    bool ok=true;
469    while(true) {
470      if(sep=='\0')
471        ok=(is>>element);
472      else
473        ok=getline(is, element, sep);
474      if(!ok)
475        break;
476      pusher(element, vec);
477    }
478  }
479
480namespace detail {
481  template<typename T>
482  bool convert(const std::string& s, T& result)
483  {
484    if (!std::numeric_limits<T>::is_signed) {
485      // first non-whitespace character
486      std::string::const_iterator iter = s.begin();
487      while (iter!=s.end() && std::isspace(*iter))
488        ++iter;
489      // unsigned int cannot start with a '-' and with some compilers
490      // operation ss >> result won't fail so catch it like this instead.
491      if (iter==s.end() || *iter=='-')
492        return false;
493    }
494    std::istringstream ss(s);
495    ss >> result;
496    if (ss.fail()) {
497      if (is_nan(s) || is_equal(s, "-nan")) {
498        result = std::numeric_limits<T>::quiet_NaN();
499        return true;
500      }
501      if (is_equal(s, "inf")) {
502        result = std::numeric_limits<T>::infinity();
503        return true;
504      }
505      if (is_equal(s, "-inf")) {
506        // unsigned types are caught in prologue
507        YAT_ASSERT(std::numeric_limits<T>::is_signed);
508        result = -std::numeric_limits<T>::infinity();
509        return true;
510      }
511      return false;
512    }
513    // Check that nothing is left on stream
514    std::string b;
515    ss >> b;
516    return b.empty();
517  }
518} // of namespace detail
519
520}}} // of namespace utility, yat, and theplu
521
522#endif
Note: See TracBrowser for help on using the repository browser.