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 Generated on Tue Dec 13 2016 16:32:41 for The Rivet MC analysis system by ![]() |