source: trunk/yat/utility.h @ 1505

Last change on this file since 1505 was 1505, checked in by Peter Johansson, 7 years ago

update to latest yat source (including Id strings)

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