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

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

merge release 0.10 into trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.5 KB
Line 
1#ifndef _theplu_yat_utility_utility_
2#define _theplu_yat_utility_utility_
3
4// $Id: utility.h 2943 2013-01-04 06:38:38Z 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  void mkdir(const std::string& dir, mode_t mode=0777);
340
341  /**
342     Similar to mkdir(const std::string&, mode_t).
343
344     No error if \a dir already exist. Make parent directories as needed.
345   */
346  void mkdir_p(const std::string& dir, mode_t mode=0777);
347
348  /**
349     same as C function remove but throws errno_error at failure
350
351     \see http://linux.die.net/man/3/remove
352   */
353  void remove(const std::string& fn);
354
355  /**
356     same as C function with same name but throws errno_error if error
357     is encountered
358
359     \see http://linux.die.net/man/3/rename
360
361     \since New in yat 0.10
362   */
363  void rename(const std::string& from, const std::string to);
364
365  /**
366     In \a full_str replace every sub-string \a old_str with \a
367     new_str;
368   */
369  void replace(std::string& full_str, std::string old_str, std::string new_str);
370
371  // template implementations
372
373  template<typename InputIterator, typename OutputIterator>
374  bool binary_weight(InputIterator first, InputIterator last, 
375                     OutputIterator result)
376  {
377    bool nan=false;
378    while (first!=last) {
379      if (std::isnan(*first)) {
380        *result=0;
381        nan=true;
382      }
383      else
384        *result = 1.0;
385      ++first;
386      ++result;
387    }
388    return nan;
389  }
390
391
392  // template implementations
393  template<typename T>
394  std::string convert(T input)
395  {
396    std::ostringstream ss;
397    ss << input;
398    return ss.str();
399  }
400
401
402  template<typename T>
403  T convert(const std::string& s)
404  {
405    T result;
406    if (!detail::convert(s, result))
407      throw runtime_error(std::string("yat::utility::convert(\"")+s+
408                          std::string("\")"));
409    return result;
410  }
411
412
413  template<typename T>
414  bool is(const std::string& s)
415  {
416    T tmp;
417    return detail::convert(s, tmp);
418  }
419
420
421  template<typename T>
422  void load(std::istream& is, std::vector<std::vector<T> >& matrix, 
423            char sep, char line_sep, bool ignore_empty, 
424            bool rectangle)
425  {
426    size_t nof_columns=0;
427    std::string line;
428    while(getline(is, line, line_sep)){
429      if (line.empty() && ignore_empty)
430        continue;
431      matrix.push_back(std::vector<T>());
432      std::vector<T>& v=matrix.back();
433      v.reserve(nof_columns);
434      std::stringstream ss(line);
435      load(ss, v, sep);
436      // add NaN for final separator (or empty string if T=std::string)
437      detail::VectorPusher<T> pusher;
438      if(sep!='\0' && !line.empty() && line[line.size()-1]==sep) 
439        pusher("", v);
440     
441      if (rectangle && nof_columns && v.size()!=nof_columns) {
442        std::ostringstream s;
443        s << "load stream error: "
444          << "line " << matrix.size() << " has " << v.size()
445          << " columns; expected " << nof_columns << " columns.";
446        throw utility::IO_error(s.str());
447      }       
448      nof_columns = std::max(nof_columns, v.size());
449    }
450
451    // manipulate the state of the stream to be good
452    is.clear(std::ios::goodbit);
453  }
454
455  template<typename T>
456  void load(std::istream& is, std::vector<T>& vec, char sep)
457  {
458    detail::VectorPusher<T> pusher;
459    std::string element;
460    bool ok=true;
461    while(true) {
462      if(sep=='\0')
463        ok=(is>>element);
464      else
465        ok=getline(is, element, sep);
466      if(!ok)
467        break;
468      pusher(element, vec);
469    }
470  }
471
472namespace detail {
473  template<typename T>
474  bool convert(const std::string& s, T& result)
475  {
476    if (!std::numeric_limits<T>::is_signed) {
477      // first non-whitespace character
478      std::string::const_iterator iter = s.begin();
479      while (iter!=s.end() && std::isspace(*iter))
480        ++iter;
481      // unsigned int cannot start with a '-' and with some compilers
482      // operation ss >> result won't fail so catch it like this instead.
483      if (iter==s.end() || *iter=='-')
484        return false;
485    }
486    std::istringstream ss(s);
487    ss >> result;
488    if (ss.fail()) {
489      if (is_nan(s)) {
490        result = std::numeric_limits<T>::quiet_NaN();
491        return true;
492      }
493      if (is_equal(s, "inf")) {
494        result = std::numeric_limits<T>::infinity();
495        return true;
496      }
497      if (is_equal(s, "-inf")) {
498        // unsigned types are caught in prologue
499        YAT_ASSERT(std::numeric_limits<T>::is_signed);
500        result = -std::numeric_limits<T>::infinity();
501        return true;
502      }
503      return false;
504    }
505    // Check that nothing is left on stream
506    std::string b;
507    ss >> b;
508    return b.empty();
509  }
510} // of namespace detail
511
512}}} // of namespace utility, yat, and theplu
513
514#endif
Note: See TracBrowser for help on using the repository browser.