rivet is hosted by Hepforge, IPPP Durham
Utils.hh
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #ifndef RIVET_Utils_HH
00003 #define RIVET_Utils_HH
00004 
00005 #include "Rivet/Tools/RivetSTL.hh"
00006 #include "Rivet/Tools/PrettyPrint.hh"
00007 #include <ostream>
00008 #include <iostream>
00009 #include <cctype>
00010 #include <cerrno>
00011 #include <stdexcept>
00012 #include <numeric>
00013 #include <limits>
00014 #include <climits>
00015 #include <cfloat>
00016 #include <cmath>
00017 
00018 
00019 // Macro to help with overzealous compiler warnings
00020 /// @note It's easier and better to just not give an arg name to args which won't be used, when possible.
00021 #ifdef UNUSED
00022 #elif defined(__GNUC__)
00023 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
00024 #elif defined(__LCLINT__)
00025 # define UNUSED(x) /*@unused@*/ x
00026 #else
00027 # define UNUSED(x) x
00028 #endif
00029 
00030 
00031 /// Macro to help mark code as deprecated to produce compiler warnings
00032 #ifndef DEPRECATED
00033 #if __GNUC__ && __cplusplus && RIVET_NO_DEPRECATION_WARNINGS == 0
00034 #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
00035 #if GCC_VERSION >= 40500
00036   #if __cplusplus > 201103L
00037   #define DEPRECATED(x) [[deprecated(x)]]
00038   #else
00039   #define DEPRECATED(x) __attribute__((deprecated(x)))
00040   #endif
00041 #else
00042   #define DEPRECATED(x) __attribute__((deprecated))
00043 #endif
00044 #else
00045   #define DEPRECATED(x)
00046 #endif
00047 #endif
00048 
00049 
00050 namespace Rivet {
00051 
00052 
00053   /// Convenient const for getting the double NaN value
00054   static constexpr double DBL_NAN = std::numeric_limits<double>::quiet_NaN();
00055 
00056 
00057   /// @name String utils
00058   //@{
00059 
00060   struct bad_lexical_cast : public std::runtime_error {
00061     bad_lexical_cast(const std::string& what) : std::runtime_error(what) {}
00062   };
00063 
00064   /// @brief Convert between any types via stringstream
00065   template<typename T, typename U>
00066   T lexical_cast(const U& in) {
00067     try {
00068       std::stringstream ss;
00069       ss << in;
00070       T out;
00071       ss >> out;
00072       return out;
00073     } catch (const std::exception& e) {
00074       throw bad_lexical_cast(e.what());
00075     }
00076   }
00077 
00078   /// @brief Convert any object to a string
00079   ///
00080   /// Just a convenience wrapper for the more general Boost lexical_cast
00081   template <typename T>
00082   inline string to_str(const T& x) {
00083     return lexical_cast<string>(x);
00084   }
00085 
00086   /// @brief Convert any object to a string
00087   ///
00088   /// An alias for to_str() with a more "Rivety" mixedCase name.
00089   template <typename T>
00090   inline string toString(const T& x) {
00091     return to_str(x);
00092   }
00093 
00094   /// Replace the first instance of patt with repl
00095   inline string& replace_first(string& str, const string& patt, const string& repl) {
00096     if (!contains(str, patt)) return str; //< contains from RivetSTL
00097     str.replace(str.find(patt), patt.size(), repl);
00098     return str;
00099   }
00100 
00101   /// @brief Replace all instances of patt with repl
00102   ///
00103   /// @note Finding is interleaved with replacement, so the second search happens after
00104   /// first replacement, etc. This could lead to infinite loops and other counterintuitive
00105   /// behaviours if not careful.
00106   inline string& replace_all(string& str, const string& patt, const string& repl) {
00107     if (!contains(str, patt)) return str; //< contains from RivetSTL
00108     while (true) {
00109       string::size_type it = str.find(patt);
00110       if (it == string::npos) break;
00111       str.replace(it, patt.size(), repl);
00112     }
00113     return str;
00114   }
00115 
00116 
00117   /// Case-insensitive string comparison function
00118   inline int nocase_cmp(const string& s1, const string& s2) {
00119     string::const_iterator it1 = s1.begin();
00120     string::const_iterator it2 = s2.begin();
00121     while ( (it1 != s1.end()) && (it2 != s2.end()) ) {
00122       if(::toupper(*it1) != ::toupper(*it2)) { // < Letters differ?
00123         // Return -1 to indicate smaller than, 1 otherwise
00124         return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1;
00125       }
00126       // Proceed to the next character in each string
00127       ++it1;
00128       ++it2;
00129     }
00130     size_t size1 = s1.size(), size2 = s2.size(); // Cache lengths
00131     // Return -1,0 or 1 according to strings' lengths
00132     if (size1 == size2) return 0;
00133     return (size1 < size2) ? -1 : 1;
00134   }
00135 
00136 
00137   /// Case-insensitive string equality function
00138   inline bool nocase_equals(const string& s1, const string& s2) {
00139     return nocase_cmp(s1, s2) == 0;
00140   }
00141 
00142 
00143   /// Convert a string to lower-case
00144   inline string toLower(const string& s) {
00145     string out = s;
00146     std::transform(out.begin(), out.end(), out.begin(), (int(*)(int)) std::tolower);
00147     return out;
00148   }
00149 
00150 
00151   /// Convert a string to upper-case
00152   inline string toUpper(const string& s) {
00153     string out = s;
00154     std::transform(out.begin(), out.end(), out.begin(), (int(*)(int)) std::toupper);
00155     return out;
00156   }
00157 
00158 
00159   /// Check whether a string @a start is found at the start of @a s
00160   inline bool startsWith(const string& s, const string& start) {
00161     if (s.length() < start.length()) return false;
00162     return s.substr(0, start.length()) == start;
00163   }
00164 
00165 
00166   /// Check whether a string @a end is found at the end of @a s
00167   inline bool endsWith(const string& s, const string& end) {
00168     if (s.length() < end.length()) return false;
00169     return s.substr(s.length() - end.length()) == end;
00170   }
00171 
00172 
00173   /// Make a string containing the string representations of each item in v, separated by sep
00174   template <typename T>
00175   inline string join(const vector<T>& v, const string& sep=" ") {
00176     string rtn;
00177     for (size_t i = 0; i < v.size(); ++i) {
00178       if (i != 0) rtn += sep;
00179       rtn += to_str(v[i]);
00180     }
00181     return rtn;
00182   }
00183 
00184   /// Make a string containing the string representations of each item in s, separated by sep
00185   template <typename T>
00186   inline string join(const set<T>& s, const string& sep=" ") {
00187     string rtn;
00188     for (const T& x : s) {
00189       if (rtn.size() > 0) rtn += sep;
00190       rtn += to_str(x);
00191     }
00192     return rtn;
00193   }
00194 
00195   //@}
00196 
00197 
00198   /// @name Path utils
00199   //@{
00200 
00201   /// @brief Split a path string with colon delimiters
00202   ///
00203   /// Ignores zero-length substrings. Designed for getting elements of filesystem paths, naturally.
00204   inline vector<string> pathsplit(const string& path) {
00205     const string delim = ":";
00206     vector<string> dirs;
00207     string tmppath = path;
00208     while (true) {
00209       const size_t delim_pos = tmppath.find(delim);
00210       if (delim_pos == string::npos) break;
00211       const string dir = tmppath.substr(0, delim_pos);
00212       if (dir.length()) dirs.push_back(dir); // Don't insert "empties"
00213       tmppath.replace(0, delim_pos+1, "");
00214     }
00215     if (tmppath.length()) dirs.push_back(tmppath); // Don't forget the trailing component!
00216     return dirs;
00217   }
00218 
00219 
00220   /// @brief Join several filesystem paths together with the standard ':' delimiter
00221   ///
00222   /// Note that this does NOT join path elements together with a platform-portable
00223   /// directory delimiter, cf. the Python @c {os.path.join}!
00224   inline string pathjoin(const vector<string>& paths) {
00225     return join(paths, ":");
00226   }
00227 
00228   //@}
00229 
00230 
00231   /// @name Container utils
00232   //@{
00233 
00234   /// Return number of elements in the container @a c for which @c f(x) is true.
00235   template <typename CONTAINER>
00236   inline unsigned int count(const CONTAINER& c) {
00237     // return std::count_if(std::begin(c), std::end(c), [](const typename CONTAINER::value_type& x){return bool(x);});
00238     unsigned int rtn = 0;
00239     for (const auto& x : c) if (bool(x)) rtn += 1;
00240     return rtn;
00241   }
00242 
00243   /// Return number of elements in the container @a c for which @c f(x) is true.
00244   template <typename CONTAINER, typename FN>
00245   inline unsigned int count(const CONTAINER& c, const FN& f) {
00246     return std::count_if(std::begin(c), std::end(c), f);
00247   }
00248 
00249   /// Return true if x is true for any x in container c, otherwise false.
00250   template <typename CONTAINER>
00251   inline bool any(const CONTAINER& c) {
00252     // return std::any_of(std::begin(c), std::end(c), [](const auto& x){return bool(x);});
00253     for (const auto& x : c) if (bool(x)) return true;
00254     return false;
00255   }
00256 
00257   /// Return true if f(x) is true for any x in container c, otherwise false.
00258   template <typename CONTAINER, typename FN>
00259   inline bool any(const CONTAINER& c, const FN& f) {
00260     return std::any_of(std::begin(c), std::end(c), f);
00261   }
00262 
00263   /// Return true if @a x is true for all @c x in container @a c, otherwise false.
00264   template <typename CONTAINER>
00265   inline bool all(const CONTAINER& c) {
00266     // return std::all_of(std::begin(c), std::end(c), [](const auto& x){return bool(x);});
00267     for (const auto& x : c) if (!bool(x)) return false;
00268     return true;
00269   }
00270 
00271   /// Return true if @a f(x) is true for all @c x in container @a c, otherwise false.
00272   template <typename CONTAINER, typename FN>
00273   inline bool all(const CONTAINER& c, const FN& f) {
00274     return std::all_of(std::begin(c), std::end(c), f);
00275   }
00276 
00277   /// Return true if @a x is false for all @c x in container @a c, otherwise false.
00278   template <typename CONTAINER>
00279   inline bool none(const CONTAINER& c) {
00280     // return std::none_of(std::begin(c), std::end(c), [](){});
00281     for (const auto& x : c) if (bool(x)) return false;
00282     return true;
00283   }
00284 
00285   /// Return true if @a f(x) is false for all @c x in container @a c, otherwise false.
00286   template <typename CONTAINER, typename FN>
00287   inline bool none(const CONTAINER& c, const FN& f) {
00288     return std::none_of(std::begin(c), std::end(c), f);
00289   }
00290 
00291 
00292   /// A single-container-arg version of std::transform, aka @c map
00293   template <typename C1, typename C2, typename FN>
00294   inline const C2& transform(const C1& in, C2& out, const FN& f) {
00295     out.clear(); out.resize(in.size());
00296     std::transform(in.begin(), in.end(), out.begin(), f);
00297     return out;
00298   }
00299 
00300   /// A single-container-arg version of std::accumulate, aka @c reduce
00301   template <typename C1, typename T, typename FN>
00302   inline T accumulate(const C1& in, const T& init, const FN& f) {
00303     const T rtn = std::accumulate(in.begin(), in.end(), init, f);
00304     return rtn;
00305   }
00306 
00307   /// Generic sum function, adding @c x for all @c x in container @a c, starting with @a start
00308   template <typename CONTAINER, typename T>
00309   inline T sum(const CONTAINER& c, const T& start=T()) {
00310     T rtn = start;
00311     for (const auto& x : c) rtn += x;
00312     return rtn;
00313   }
00314 
00315   /// Generic sum function, adding @a fn(@c x) for all @c x in container @a c, starting with @a start
00316   template <typename CONTAINER, typename FN, typename T>
00317   inline T sum(const CONTAINER& c, const FN& f, const T& start=T()) {
00318     T rtn = start;
00319     for (const auto& x : c) rtn += f(x);
00320     return rtn;
00321   }
00322 
00323 
00324   /// Filter a collection in-place, removing the subset that passes the supplied function
00325   template <typename CONTAINER, typename FN>
00326   inline CONTAINER& ifilter_discard(CONTAINER& c, const FN& f) {
00327     const auto newend = std::remove_if(std::begin(c), std::end(c), f);
00328     c.erase(newend, c.end());
00329     return c;
00330   }
00331 
00332   /// Filter a collection by copy, removing the subset that passes the supplied function
00333   template <typename CONTAINER, typename FN>
00334   inline CONTAINER filter_discard(const CONTAINER& c, const FN& f) {
00335     CONTAINER rtn = c;
00336     return ifilter_discard(rtn, f); ///< @todo More efficient would be copy_if with back_inserter...
00337   }
00338 
00339   /// Filter a collection by copy into a supplied container, removing the subset that passes the supplied function
00340   /// @note New container will be replaced, not appended to
00341   template <typename CONTAINER, typename FN>
00342   inline CONTAINER& filter_discard(const CONTAINER& c, const FN& f, CONTAINER& out) {
00343     out = filter_discard(c, f);
00344     return out;
00345   }
00346 
00347 
00348   /// Filter a collection in-place, keeping the subset that passes the supplied function
00349   template <typename CONTAINER, typename FN>
00350   inline CONTAINER& ifilter_select(CONTAINER& c, const FN& f) {
00351     //using value_type = typename std::remove_reference<decltype(*std::begin(std::declval<typename std::add_lvalue_reference<CONTAINER>::type>()))>::type;
00352     const auto invf = [&](const typename CONTAINER::value_type& x){ return !f(x); };
00353     return ifilter_discard(c, invf);
00354   }
00355 
00356   /// Filter a collection by copy, keeping the subset that passes the supplied function
00357   template <typename CONTAINER, typename FN>
00358   inline CONTAINER filter_select(const CONTAINER& c, const FN& f) {
00359     CONTAINER rtn = c;
00360     return ifilter_select(rtn, f); ///< @todo More efficient would be copy_if with back_inserter ... but is that equally container agnostic?
00361   }
00362 
00363   /// Filter a collection by copy into a supplied container, keeping the subset that passes the supplied function
00364   /// @note New container will be replaced, not appended to
00365   template <typename CONTAINER, typename FN>
00366   inline CONTAINER& filter_select(const CONTAINER& c, const FN& f, CONTAINER& out) {
00367     out = filter_select(c, f);
00368     return out;
00369   }
00370 
00371 
00372   /// @brief Slice of the container elements cf. Python's [i:j] syntax
00373   ///
00374   /// The element at the @j index is not included in the returned container.
00375   /// @a i and @a j can be negative, treated as backward offsets from the end of the container.
00376   template <typename CONTAINER>
00377   inline CONTAINER slice(const CONTAINER& c, int i, int j) {
00378     CONTAINER rtn;
00379     const size_t off1 = (i >= 0) ? i : c.size() + i;
00380     const size_t off2 = (j >= 0) ? j : c.size() + j;
00381     if (off1 > c.size() || off2 > c.size()) throw RangeError("Attempting to slice beyond requested offsets");
00382     if (off2 < off1) throw RangeError("Requested offsets in invalid order");
00383     rtn.resize(off2 - off1);
00384     std::copy(c.begin()+off1, c.begin()+off2, rtn.begin());
00385     return rtn;
00386   }
00387 
00388   /// @brief Tail slice of the container elements cf. Python's [i:] syntax
00389   ///
00390   /// Single-index specialisation of @c slice(c, i, j)
00391   template <typename CONTAINER>
00392   inline CONTAINER slice(const CONTAINER& c, int i) {
00393     return slice(c, i, c.size());
00394   }
00395 
00396   /// @brief Head slice of the @a n first container elements
00397   ///
00398   /// Negative @a n means to take the head excluding the @a{n}-element tail
00399   template <typename CONTAINER>
00400   inline CONTAINER head(const CONTAINER& c, int n) {
00401     // if (n > c.size()) throw RangeError("Requested head longer than container");
00402     if (n < 0) n = std::max(0, (int)c.size()+n);
00403     n = std::min(n, (int)c.size());
00404     return slice(c, 0, n);
00405   }
00406 
00407   /// @brief Tail slice of the @a n last container elements
00408   ///
00409   /// Negative @a n means to take the tail from after the @a{n}th element
00410   template <typename CONTAINER>
00411   inline CONTAINER tail(const CONTAINER& c, int n) {
00412     // if (n > c.size()) throw RangeError("Requested tail longer than container");
00413     if (n < 0) n = std::max(0, (int)c.size()+n);
00414     n = std::min(n, (int)c.size());
00415     return slice(c, c.size()-n);
00416   }
00417 
00418 
00419   using std::min;
00420   using std::max;
00421 
00422   /// Find the minimum value in the vector
00423   inline double min(const vector<double>& in, double errval=DBL_NAN) {
00424     return *std::min_element(in.begin(), in.end());
00425   }
00426 
00427   /// Find the maximum value in the vector
00428   inline double max(const vector<double>& in, double errval=DBL_NAN) {
00429     const auto e = std::max_element(in.begin(), in.end());
00430     return e != in.end() ? *e : errval;
00431   }
00432 
00433   /// Find the minimum and maximum values in the vector
00434   inline pair<double,double> minmax(const vector<double>& in, double errval=DBL_NAN) {
00435     const auto e = std::minmax_element(in.begin(), in.end());
00436     const double rtnmin = e.first != in.end() ? *e.first : errval;
00437     const double rtnmax = e.second != in.end() ? *e.first : errval;
00438     return std::make_pair(rtnmin, rtnmax);
00439   }
00440 
00441 
00442   /// Find the minimum value in the vector
00443   inline int min(const vector<int>& in, int errval=-1) {
00444     const auto e = std::min_element(in.begin(), in.end());
00445     return e != in.end() ? *e : errval;
00446   }
00447 
00448   /// Find the maximum value in the vector
00449   inline int max(const vector<int>& in, int errval=-1) {
00450     const auto e = std::max_element(in.begin(), in.end());
00451     return e != in.end() ? *e : errval;
00452   }
00453 
00454   /// Find the minimum and maximum values in the vector
00455   inline pair<int,int> minmax(const vector<int>& in, int errval=-1) {
00456     const auto e = std::minmax_element(in.begin(), in.end());
00457     const double rtnmin = e.first != in.end() ? *e.first : errval;
00458     const double rtnmax = e.second != in.end() ? *e.first : errval;
00459     return std::make_pair(rtnmin, rtnmax);
00460   }
00461 
00462   //@}
00463 
00464 
00465 }
00466 
00467 #endif