File indexing completed on 2026-05-16 08:31:31
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
0058
0059
0060 template <typename NUM>
0061 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
0062 isZero(NUM val, double tolerance=1e-5) {
0063 return fabs(val) < tolerance;
0064 }
0065
0066
0067
0068
0069
0070 template <typename NUM>
0071 inline typename std::enable_if_t<std::is_integral_v<NUM>, bool>
0072 isZero(NUM val, double = 1e-5) {
0073 return val==0;
0074 }
0075
0076
0077 template <typename NUM>
0078 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
0079 isNaN(NUM val) { return std::isnan(val); }
0080
0081
0082 template <typename NUM>
0083 inline typename std::enable_if_t<std::is_floating_point_v<NUM>, bool>
0084 notNaN(NUM val) { return !std::isnan(val); }
0085
0086
0087
0088
0089
0090
0091 template <typename N1, typename N2>
0092 inline typename std::enable_if_t<
0093 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2> &&
0094 (std::is_floating_point_v<N1> || std::is_floating_point_v<N2>), bool>
0095 fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) {
0096 const double absavg = (std::abs(a) + std::abs(b))/2.0;
0097 const double absdiff = std::abs(a - b);
0098 const bool rtn = (isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
0099 return rtn;
0100 }
0101
0102
0103
0104
0105
0106 template <typename N1, typename N2>
0107 inline typename std::enable_if_t<
0108 std::is_integral_v<N1> && std::is_integral_v<N2>, bool>
0109 fuzzyEquals(N1 a, N2 b, double) {
0110 return a == b;
0111 }
0112
0113
0114 static std::function<bool(const double, const double)> fuzzyEqComp =
0115 [](const double& lhs, const double& rhs) { return fuzzyEquals(lhs, rhs); };
0116
0117
0118
0119
0120
0121 template <typename N1, typename N2>
0122 inline typename std::enable_if_t<
0123 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>, bool>
0124 fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) {
0125 return a > b || fuzzyEquals(a, b, tolerance);
0126 }
0127
0128
0129
0130
0131 template <typename N1, typename N2>
0132 inline typename std::enable_if_t<
0133 std::is_arithmetic_v<N1> && std::is_arithmetic_v<N2>, bool>
0134 fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) {
0135 return a < b || fuzzyEquals(a, b, tolerance);
0136 }
0137
0138
0139 inline double approx(double a, int n = 5) {
0140 double roundTo = pow(10.0,n);
0141 a *= roundTo;
0142 a = floor(a);
0143 return a/roundTo;
0144 }
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };
0156
0157
0158
0159
0160
0161
0162 template<typename NUM>
0163 inline bool inRange(NUM value, NUM low, NUM high,
0164 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0165 if (lowbound == OPEN && highbound == OPEN) {
0166 return (value > low && value < high);
0167 } else if (lowbound == OPEN && highbound == CLOSED) {
0168 return (value > low && value <= high);
0169 } else if (lowbound == CLOSED && highbound == OPEN) {
0170 return (value >= low && value < high);
0171 } else {
0172 return (value >= low && value <= high);
0173 }
0174 }
0175
0176
0177 template<typename NUM>
0178 inline bool inRange(NUM value, std::pair<NUM, NUM> lowhigh,
0179 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0180 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
0181 }
0182
0183
0184
0185
0186
0187
0188 inline bool inRange(int value, int low, int high,
0189 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=CLOSED) {
0190 if (lowbound == OPEN && highbound == OPEN) {
0191 return (value > low && value < high);
0192 } else if (lowbound == OPEN && highbound == CLOSED) {
0193 return (value > low && value <= high);
0194 } else if (lowbound == CLOSED && highbound == OPEN) {
0195 return (value >= low && value < high);
0196 } else {
0197 return (value >= low && value <= high);
0198 }
0199 }
0200
0201
0202 inline bool inRange(int value, std::pair<int, int> lowhigh,
0203 RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
0204 return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
0205 }
0206
0207
0208
0209
0210
0211
0212
0213
0214 template <typename NUM>
0215 inline NUM sqr(NUM a) {
0216 return a*a;
0217 }
0218
0219
0220 template <typename... Num>
0221 inline std::enable_if_t<std::conjunction_v<std::is_arithmetic<Num>...>, double>
0222 add_quad(Num ... vals) {
0223 return sqrt((0.0 + ... + sqr(vals)));
0224 }
0225
0226
0227 inline int sign(double val) {
0228 if (isZero(val)) return ZERO;
0229 const int valsign = (val > 0) ? PLUS : MINUS;
0230 return valsign;
0231 }
0232
0233
0234 inline int sign(int val) {
0235 if (val == 0) return ZERO;
0236 return (val > 0) ? PLUS : MINUS;
0237 }
0238
0239
0240 inline int sign(long val) {
0241 if (val == 0) return ZERO;
0242 return (val > 0) ? PLUS : MINUS;
0243 }
0244
0245
0246 inline double subtract(double a, double b, double tolerance = 1e-5) {
0247 if (fuzzyEquals(a,b,tolerance)) return 0.;
0248 return a - b;
0249 }
0250
0251
0252 inline double add(double a, double b, double tolerance = 1e-5) {
0253 return subtract(a,-b,tolerance);
0254 }
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266 inline std::vector<double> linspace(size_t nbins, double xmin, double xmax, bool include_end=true) {
0267 if (xmax < xmin) throw RangeError("xmax should not be smaller than xmin!");
0268 if (nbins == 0) throw RangeError("Requested number of bins is 0!");
0269 std::vector<double> rtn;
0270 const double interval = (xmax-xmin)/static_cast<double>(nbins);
0271 for (size_t i = 0; i < nbins; ++i) {
0272 rtn.push_back(xmin + i*interval);
0273 }
0274 assert(rtn.size() == nbins);
0275 if (include_end) rtn.push_back(xmax);
0276 return rtn;
0277 }
0278
0279
0280
0281
0282
0283
0284
0285 inline std::vector<double> logspace(size_t nbins, double xmin, double xmax, bool include_end=true) {
0286 if (xmax < xmin) throw RangeError("xmax should not be smaller than xmin!");
0287 if (xmin < 0) throw RangeError("xmin should not be negative!");
0288 if (nbins == 0) throw RangeError("Requested number of bins is 0!");
0289 const double logxmin = std::log(xmin);
0290 const double logxmax = std::log(xmax);
0291 const std::vector<double> logvals = linspace(nbins, logxmin, logxmax);
0292 assert(logvals.size() == nbins+1);
0293 std::vector<double> rtn; rtn.reserve(logvals.size());
0294 rtn.push_back(xmin);
0295 for (size_t i = 1; i < logvals.size()-1; ++i) {
0296 rtn.push_back(std::exp(logvals[i]));
0297 }
0298 assert(rtn.size() == nbins);
0299 if (include_end) rtn.push_back(xmax);
0300 return rtn;
0301 }
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327 inline std::vector<double> pdfspace(size_t nbins, double xmin, double xmax, std::function<double(double)>& fn, size_t nsample=10000) {
0328 const double dx = (xmax-xmin)/(double)nsample;
0329 const std::vector<double> xs = linspace(nsample, xmin, xmax);
0330 std::vector<double> ys(0, nsample);
0331 auto posfn = [&](double x){return std::max(fn(x), 0.0);};
0332 std::transform(xs.begin(), xs.end(), ys.begin(), posfn);
0333 std::vector<double> areas; areas.reserve(nsample);
0334 double areasum = 0;
0335 for (size_t i = 0; i < ys.size()-1; ++i) {
0336 const double area = (ys[i] + ys[i+1])*dx/2.0;
0337 areas[i] = area;
0338 areasum += area;
0339 }
0340 const double df = areasum/(double)nbins;
0341 std::vector<double> xedges{xmin}; xedges.reserve(nbins+1);
0342 double fsum = 0;
0343 for (size_t i = 0; i < nsample-1; ++i) {
0344 fsum += areas[i];
0345 if (fsum > df) {
0346 fsum = 0;
0347 xedges.push_back(xs[i+1]);
0348 }
0349 }
0350 xedges.push_back(xmax);
0351 assert(xedges.size() == nbins+1);
0352 return xedges;
0353 }
0354
0355
0356
0357
0358
0359 template <typename NUM>
0360 inline int index_between(const NUM& val, const std::vector<NUM>& binedges) {
0361 if (!inRange(val, binedges.front(), binedges.back())) return -1;
0362 int index = -1;
0363 for (size_t i = 1; i < binedges.size(); ++i) {
0364 if (val < binedges[i]) {
0365 index = i-1;
0366 break;
0367 }
0368 }
0369 assert(inRange(index, -1, binedges.size()-1));
0370 return index;
0371 }
0372
0373
0374
0375 }
0376
0377 #endif