source: trunk/yat/utility.h @ 1434

Last change on this file since 1434 was 1422, checked in by Peter Johansson, 9 years ago

latest yat

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