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

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

add functions mkdir, mkdir_p, basename, dirname. refs #717

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.5 KB
Line 
1#ifndef _theplu_yat_utility_utility_
2#define _theplu_yat_utility_utility_
3
4// $Id: utility.h 2892 2012-12-09 07:15:18Z 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 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     \return everything after last '/'
59   */
60  std::string basename(const std::string& fn);
61
62  /**
63     For each element in resulting range assign it to 0.0 if
64     corresponding element in input range is NaN else assign it to
65     1.0.
66
67     \return true if there is at least one NaN in input range
68     [first, last).
69
70     \since New in yat 0.5
71  */
72  template<typename InputIterator, typename OutputIterator>
73  bool binary_weight(InputIterator first, InputIterator last, 
74                     OutputIterator result);
75
76  /**
77     \brief convert T to a string
78
79     T is supposed to be a numerical type.
80
81     \since new in yat 0.8
82   */
83  template<typename T>
84  std::string convert(T input);
85
86  /**
87     \brief convert string to (numerical) type
88
89     \throw runtime_error if conversion fails
90   */
91  template<typename T>
92  T convert(const std::string& s);
93
94  /**
95     \return everything prior last '/'.
96   */
97  std::string dirname(const std::string& fn);
98
99  /**
100     \brief check if string is convertible to (numerical) type \c T
101
102     \since New in yat 0.5
103   */
104  template<typename T>
105  bool is(const std::string& s);
106
107  ///
108  /// @return true if string is a double
109  ///
110  /// \deprecated Provided for backward compatibility with the 0.4
111  /// API. Use is<double>(const std::string&)
112  ///
113  bool is_double(const std::string&) YAT_DEPRECATE;
114
115  /**
116     This function takes the first word (separated by whitespace) in
117     \a s, replaces all upper case with lower case, and compares it
118     with \a other.
119
120     \return true if processed \a s is equal to \a other. It returns
121     false otherwise or if \a s contains more than one word.
122  */
123  bool is_equal(std::string s, std::string other);
124
125  ///
126  /// @return true if string is a float
127  ///
128  /// \deprecated Provided for backward compatibility with the 0.4
129  /// API. Use is<float>(const std::string&)
130  ///
131  bool is_float(const std::string&) YAT_DEPRECATE;
132
133  ///
134  /// @return true if string is an int
135  ///
136  /// \deprecated Provided for backward compatibility with the 0.4
137  /// API. Use is<int>(const std::string&)
138  ///
139  bool is_int(const std::string&) YAT_DEPRECATE;
140
141  ///
142  /// @return true if string is "nan" (case-insensitive)
143  ///
144  bool is_nan(const std::string& s);
145
146  /**
147     The std::istream will be interpreted as outlined here:
148
149     Lines are separated by character \a line_sep and rows are
150     separated by character \a sep.
151     
152     The first line is read into a stringstream, which is used to
153     load the first vector (vec[0]) with elements using
154     load(stringstream, vec[0], sep).
155     
156     Therefore, column elements separation has two modes depending
157     on the value of \a sep.
158     
159     - If \a sep is the default '\\0' value then column elements are
160     separated with white space characters except the new line
161     character. Multiple sequential white space characters are treated
162     as one separator.
163     
164     - Setting \a sep to something else than the default value will
165     change the behaviour to use the \a sep character as the separator
166     between column elements. Multiple sequential \a sep characters
167     will be treated as separating elements with missing values.
168
169     If \a rectangle is true, rows must contain same number of
170     elements or function will throw.
171
172     If \a ignore_empty is true empty lines are ignored.
173
174     \see load(std::istream&, std::vector<T>&, char sep='\\0')
175
176     \note Requirement on T: utility::convert<T> must be supported
177     (from yat 0.7 T=string is also supported)
178
179     \since New in yat 0.6
180   */
181  template<typename T>
182  void load(std::istream& is, std::vector<std::vector<T> >& vec, char sep='\0', 
183            char line_sep='\n', bool ignore_empty=false, bool rectangle=true);
184
185  /**
186     \brief Fill a vector<T> with elements from istream
187
188     Element separation has two modes depending on the value of \a
189     sep.
190     
191     - If \a sep is the default '\\0' value then elements are
192     separated with white space characters. Multiple sequential white
193     space characters are treated as one separator.
194     
195     - Setting \a sep to something else than the default value will
196     change the behaviour to use the \a sep character as the
197     separator between column elements. Multiple sequential \a sep
198     characters will be treated as separating elements with missing
199     values. Missing values are set to std::numeric_limits<T>::quiet_NaN
200     
201     \note Requirement on T: utility::convert<T> must be supported
202     (from yat 0.7 T=string is also supported)
203
204     \since New in yat 0.6
205   */
206  template<typename T>
207  void load(std::istream& is, std::vector<T>& vec, char sep='\0');
208
209  /**
210     \return base-2 logarithm of x
211
212     \since New in yat 0.10
213   */
214  // c++11 provides std::log2 so perhaps we should call that one if
215  // availalable (but a bit tricky since this is a public header)
216  template<typename T>
217  T log2(T x) { return std::log(x)/M_LN2; }
218
219// private namespace
220namespace detail {
221
222  /**
223     \brief convert s to t
224
225     used in function is<T> and convert<T>
226
227     \return true if conversion was successful
228
229     \internal
230   */
231  template<typename T>
232  bool convert(const std::string& s, T& t);
233
234  /**
235     Functor used in load function
236   */
237  template<typename T>
238  struct VectorPusher
239  {
240    /**
241       convert element to T and push on vec's back
242
243       \internal
244     */
245    void operator()(const std::string& element, std::vector<T>& vec)
246    { 
247      if (!element.size())
248        vec.push_back(std::numeric_limits<T>::quiet_NaN());
249      else {
250        vec.push_back(theplu::yat::utility::convert<T>(element));
251      }
252    }
253  };
254
255  /**
256     specialization for string
257
258     \internal
259   */
260  template<>
261  struct VectorPusher<std::string>
262  {
263    /**
264       push element on vec's back
265     */
266    void operator()(const std::string& element, std::vector<std::string>& vec)
267    { 
268      vec.push_back(element);
269    }
270  };
271
272} // end of namespace detail
273
274
275  /**
276     create a directory \a dir
277
278     \throw if creation failed
279   */
280  void mkdir(const std::string& dir);
281
282  /**
283     Similar to 'mkdir -p'. Does not throw on error and create parent
284     directories if missing.
285   */
286  void mkdir_p(const std::string& dir);
287
288  // template implementations
289
290  template<typename InputIterator, typename OutputIterator>
291  bool binary_weight(InputIterator first, InputIterator last, 
292                     OutputIterator result)
293  {
294    bool nan=false;
295    while (first!=last) {
296      if (std::isnan(*first)) {
297        *result=0;
298        nan=true;
299      }
300      else
301        *result = 1.0;
302      ++first;
303      ++result;
304    }
305    return nan;
306  }
307
308
309  // template implementations
310  template<typename T>
311  std::string convert(T input)
312  {
313    std::ostringstream ss;
314    ss << input;
315    return ss.str();
316  }
317
318
319  template<typename T>
320  T convert(const std::string& s)
321  {
322    T result;
323    if (!detail::convert(s, result))
324      throw runtime_error(std::string("yat::utility::convert(\"")+s+
325                          std::string("\")"));
326    return result;
327  }
328
329
330  template<typename T>
331  bool is(const std::string& s)
332  {
333    T tmp;
334    return detail::convert(s, tmp);
335  }
336
337
338  template<typename T>
339  void load(std::istream& is, std::vector<std::vector<T> >& matrix, 
340            char sep, char line_sep, bool ignore_empty, 
341            bool rectangle)
342  {
343    size_t nof_columns=0;
344    std::string line;
345    while(getline(is, line, line_sep)){
346      if (line.empty() && ignore_empty)
347        continue;
348      matrix.push_back(std::vector<T>());
349      std::vector<T>& v=matrix.back();
350      v.reserve(nof_columns);
351      std::stringstream ss(line);
352      load(ss, v, sep);
353      // add NaN for final separator (or empty string if T=std::string)
354      detail::VectorPusher<T> pusher;
355      if(sep!='\0' && !line.empty() && line[line.size()-1]==sep) 
356        pusher("", v);
357     
358      if (rectangle && nof_columns && v.size()!=nof_columns) {
359        std::ostringstream s;
360        s << "load stream error: "
361          << "line " << matrix.size() << " has " << v.size()
362          << " columns; expected " << nof_columns << " columns.";
363        throw utility::IO_error(s.str());
364      }       
365      nof_columns = std::max(nof_columns, v.size());
366    }
367
368    // manipulate the state of the stream to be good
369    is.clear(std::ios::goodbit);
370  }
371
372  template<typename T>
373  void load(std::istream& is, std::vector<T>& vec, char sep)
374  {
375    detail::VectorPusher<T> pusher;
376    std::string element;
377    bool ok=true;
378    while(true) {
379      if(sep=='\0')
380        ok=(is>>element);
381      else
382        ok=getline(is, element, sep);
383      if(!ok)
384        break;
385      pusher(element, vec);
386    }
387  }           
388
389
390namespace detail {
391  template<typename T>
392  bool convert(const std::string& s, T& result)
393  {
394    if (!std::numeric_limits<T>::is_signed) {
395      // first non-whitespace character
396      std::string::const_iterator iter = s.begin();
397      while (iter!=s.end() && std::isspace(*iter))
398        ++iter;
399      // unsigned int cannot start with a '-' and with some compilers
400      // operation ss >> result won't fail so catch it like this instead.
401      if (iter==s.end() || *iter=='-')
402        return false;
403    }
404    std::istringstream ss(s);
405    ss >> result;
406    if (ss.fail()) {
407      if (is_nan(s)) {
408        result = std::numeric_limits<T>::quiet_NaN();
409        return true;
410      }
411      if (is_equal(s, "inf")) {
412        result = std::numeric_limits<T>::infinity();
413        return true;
414      }
415      if (is_equal(s, "-inf")) {
416        // unsigned types are caught in prologue
417        YAT_ASSERT(std::numeric_limits<T>::is_signed);
418        result = -std::numeric_limits<T>::infinity();
419        return true;
420      }
421      return false;
422    }
423    // Check that nothing is left on stream
424    std::string b;
425    ss >> b;
426    return b.empty();
427  }
428} // of namespace detail
429
430}}} // of namespace utility, yat, and theplu
431
432#endif
Note: See TracBrowser for help on using the repository browser.