File indexing completed on 2026-11-12 11:07:52
0001
0002
0003
0004
0005
0006 #ifndef YODA_MathUtils_H
0007 #define YODA_MathUtils_H
0008
0009
0010
0011 #include "YODA/Exceptions.h"
0012 #include "YODA/Config/BuildConfig.h"
0013
0014 #include <algorithm>
0015 #include <cassert>
0016 #include <cfloat>
0017 #include <climits>
0018 #include <cmath>
0019 #include <functional>
0020 #include <iostream>
0021 #include <limits>
0022 #include <map>
0023 #include <numeric>
0024 #include <numeric>
0025 #include <ostream>
0026 #include <sstream>
0027 #include <stdexcept>
0028 #include <string>
0029 #include <utility>
0030 #include <vector>
0031
0032 namespace YODA {
0033
0034
0035
0036
0037 const static double MAXDOUBLE = DBL_MAX;
0038 const static double MAXINT = INT_MAX;
0039
0040
0041 static const double PI = M_PI;
0042
0043
0044 static const double TWOPI = 2*M_PI;
0045
0046
0047 static const double HALFPI = M_PI_2;
0048
0049
0050 enum Sign { MINUS = -1, ZERO = 0, PLUS = 1 };
0051
0052
0053
0054
0055
0056
0057 template <typename NUM, typename = std::enable_if_t<std::is_arithmetic_v<NUM>>>
0058 inline bool isZero(NUM val, double tolerance=1e-5) {
0059 if constexpr(std::is_integral_v<NUM>) {
0060 return val==0;
0061 }
0062 else {
0063 return fabs(val) < tolerance;
0064 }
0065 }
0066
0067
0068 template <typename NUM, typename = std::enable_if_t<std::is_floating_point_v<NUM>>>
0069 inline bool isNaN(NUM val) { return std::isnan(val); }
0070
0071
0072 template <typename NUM, typename = std::enable_if_t<std::is_floating_point_v<NUM>>>
0073 inline bool notNaN(NUM val) { return !std::isnan(val); }
0074
0075
0076 template <typename N1,
0077 typename N2,
0078 typename = std::enable_if_t<std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>>>
0079 inline bool fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) {
0080 if constexpr(std::is_integral_v<N1> && std::is_integral_v<N2>) {
0081 return a == b;
0082 }
0083 else {
0084 const double absavg = (std::abs(a) + std::abs(b))/2.0;
0085 const double absdiff = std::abs(a - b);
0086 const bool rtn = (isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
0087 return rtn;
0088 }
0089 }
0090
0091
0092 static std::function<bool(const double, const double)> fuzzyEqComp =
0093 [](const double& lhs, const double& rhs) { return fuzzyEquals(lhs, rhs); };
0094
0095
0096
0097
0098
0099 template <typename N1,
0100 typename N2,
0101 typename = std::enable_if_t<std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>>>
0102 inline bool fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) {
0103 return a > b || fuzzyEquals(a, b, tolerance);
0104 }
0105
0106
0107
0108
0109 template <typename N1,
0110 typename N2,
0111 typename = std::enable_if_t<std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>>>
0112 inline bool fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) {
0113 return a < b || fuzzyEquals(a, b, tolerance);
0114 }
0115
0116
0117 inline double approx(double a, int n = 5) {
0118 double roundTo = pow(10.0,n);
0119 a *= roundTo;
0120 a = floor(a);
0121 return a/roundTo;
0122 }
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133 enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };
0134
0135
0136
0137
0138
0139
0140 template<typename NUM>
0141 inline bool inRange(NUM value, NUM low, NUM high,
0142 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0143 if (lowbound == OPEN && highbound == OPEN) {
0144 return (value > low && value < high);
0145 } else if (lowbound == OPEN && highbound == CLOSED) {
0146 return (value > low && value <= high);
0147 } else if (lowbound == CLOSED && highbound == OPEN) {
0148 return (value >= low && value < high);
0149 } else {
0150 return (value >= low && value <= high);
0151 }
0152 }
0153
0154
0155 template<typename NUM>
0156 inline bool inRange(NUM value, std::pair<NUM, NUM> lowhigh,
0157 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0158 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
0159 }
0160
0161
0162
0163
0164
0165
0166 inline bool inRange(int value, int low, int high,
0167 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=CLOSED) {
0168 if (lowbound == OPEN && highbound == OPEN) {
0169 return (value > low && value < high);
0170 } else if (lowbound == OPEN && highbound == CLOSED) {
0171 return (value > low && value <= high);
0172 } else if (lowbound == CLOSED && highbound == OPEN) {
0173 return (value >= low && value < high);
0174 } else {
0175 return (value >= low && value <= high);
0176 }
0177 }
0178
0179
0180 inline bool inRange(int value, std::pair<int, int> lowhigh,
0181 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0182 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
0183 }
0184
0185
0186
0187
0188
0189
0190
0191
0192 template <typename NUM>
0193 inline NUM sqr(NUM a) {
0194 return a*a;
0195 }
0196
0197
0198 template <typename... Num, typename = std::enable_if_t<std::conjunction_v<std::is_arithmetic<Num>...>>>
0199 inline double add_quad(Num ... vals) {
0200 return sqrt((0.0 + ... + sqr(vals)));
0201 }
0202
0203
0204 inline int sign(double val) {
0205 if (isZero(val)) return ZERO;
0206 const int valsign = (val > 0) ? PLUS : MINUS;
0207 return valsign;
0208 }
0209
0210
0211 inline int sign(int val) {
0212 if (val == 0) return ZERO;
0213 return (val > 0) ? PLUS : MINUS;
0214 }
0215
0216
0217 inline int sign(long val) {
0218 if (val == 0) return ZERO;
0219 return (val > 0) ? PLUS : MINUS;
0220 }
0221
0222
0223 inline double subtract(double a, double b, double tolerance = 1e-5) {
0224 if (fuzzyEquals(a,b,tolerance)) return 0.;
0225 return a - b;
0226 }
0227
0228
0229 inline double add(double a, double b, double tolerance = 1e-5) {
0230 return subtract(a,-b,tolerance);
0231 }
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243 inline std::vector<double> linspace(size_t nbins, double xmin, double xmax, bool include_end=true) {
0244 if (xmax < xmin) throw RangeError("xmax should not be smaller than xmin!");
0245 if (nbins == 0) throw RangeError("Requested number of bins is 0!");
0246 std::vector<double> rtn;
0247 const double interval = (xmax-xmin)/static_cast<double>(nbins);
0248 for (size_t i = 0; i < nbins; ++i) {
0249 rtn.push_back(xmin + i*interval);
0250 }
0251 assert(rtn.size() == nbins);
0252 if (include_end) rtn.push_back(xmax);
0253 return rtn;
0254 }
0255
0256
0257
0258
0259
0260
0261
0262 inline std::vector<double> logspace(size_t nbins, double xmin, double xmax, bool include_end=true) {
0263 if (xmax < xmin) throw RangeError("xmax should not be smaller than xmin!");
0264 if (xmin < 0) throw RangeError("xmin should not be negative!");
0265 if (nbins == 0) throw RangeError("Requested number of bins is 0!");
0266 const double logxmin = std::log(xmin);
0267 const double logxmax = std::log(xmax);
0268 const std::vector<double> logvals = linspace(nbins, logxmin, logxmax);
0269 assert(logvals.size() == nbins+1);
0270 std::vector<double> rtn; rtn.reserve(logvals.size());
0271 rtn.push_back(xmin);
0272 for (size_t i = 1; i < logvals.size()-1; ++i) {
0273 rtn.push_back(std::exp(logvals[i]));
0274 }
0275 assert(rtn.size() == nbins);
0276 if (include_end) rtn.push_back(xmax);
0277 return rtn;
0278 }
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304 inline std::vector<double> pdfspace(size_t nbins, double xmin, double xmax, std::function<double(double)>& fn, size_t nsample=10000) {
0305 const double dx = (xmax-xmin)/(double)nsample;
0306 const std::vector<double> xs = linspace(nsample, xmin, xmax);
0307 std::vector<double> ys(0, nsample);
0308 auto posfn = [&](double x){return std::max(fn(x), 0.0);};
0309 std::transform(xs.begin(), xs.end(), ys.begin(), posfn);
0310 std::vector<double> areas; areas.reserve(nsample);
0311 double areasum = 0;
0312 for (size_t i = 0; i < ys.size()-1; ++i) {
0313 const double area = (ys[i] + ys[i+1])*dx/2.0;
0314 areas[i] = area;
0315 areasum += area;
0316 }
0317 const double df = areasum/(double)nbins;
0318 std::vector<double> xedges{xmin}; xedges.reserve(nbins+1);
0319 double fsum = 0;
0320 for (size_t i = 0; i < nsample-1; ++i) {
0321 fsum += areas[i];
0322 if (fsum > df) {
0323 fsum = 0;
0324 xedges.push_back(xs[i+1]);
0325 }
0326 }
0327 xedges.push_back(xmax);
0328 assert(xedges.size() == nbins+1);
0329 return xedges;
0330 }
0331
0332
0333
0334
0335
0336 template <typename NUM>
0337 inline int index_between(const NUM& val, const std::vector<NUM>& binedges) {
0338 if (!inRange(val, binedges.front(), binedges.back())) return -1;
0339 int index = -1;
0340 for (size_t i = 1; i < binedges.size(); ++i) {
0341 if (val < binedges[i]) {
0342 index = i-1;
0343 break;
0344 }
0345 }
0346 assert(inRange(index, -1, binedges.size()-1));
0347 return index;
0348 }
0349
0350
0351
0352 }
0353
0354 #endif