rivet is hosted by Hepforge, IPPP Durham
MathUtils.hh
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #ifndef RIVET_MathUtils_HH
00003 #define RIVET_MathUtils_HH
00004 
00005 #include "Rivet/Math/MathHeader.hh"
00006 #include <type_traits>
00007 #include <cassert>
00008 
00009 namespace Rivet {
00010 
00011 
00012   /// @name Comparison functions for safe (floating point) equality tests
00013   //@{
00014 
00015   /// @brief Compare a number to zero
00016   ///
00017   /// This version for floating point types has a degree of fuzziness expressed
00018   /// by the absolute @a tolerance parameter, for floating point safety.
00019   template <typename NUM>
00020   inline typename std::enable_if<std::is_floating_point<NUM>::value, bool>::type
00021   isZero(NUM val, double tolerance=1e-8) {
00022     return fabs(val) < tolerance;
00023   }
00024 
00025   /// @brief Compare a number to zero
00026   ///
00027   /// SFINAE template specialisation for integers, since there is no FP
00028   /// precision issue.
00029   template <typename NUM>
00030   inline typename std::enable_if<std::is_integral<NUM>::value, bool>::type
00031     isZero(NUM val, double UNUSED(tolerance)=1e-8) {
00032     return val == 0;
00033   }
00034 
00035 
00036   /// @brief Compare two numbers for equality with a degree of fuzziness
00037   ///
00038   /// This version for floating point types (if any argument is FP) has a degree
00039   /// of fuzziness expressed by the fractional @a tolerance parameter, for
00040   /// floating point safety.
00041   template <typename N1, typename N2>
00042   inline typename std::enable_if<
00043     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value &&
00044    (std::is_floating_point<N1>::value || std::is_floating_point<N2>::value), bool>::type
00045   fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) {
00046     const double absavg = (std::abs(a) + std::abs(b))/2.0;
00047     const double absdiff = std::abs(a - b);
00048     const bool rtn = (isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
00049     return rtn;
00050   }
00051 
00052   /// @brief Compare two numbers for equality with a degree of fuzziness
00053   ///
00054   /// Simpler SFINAE template specialisation for integers, since there is no FP
00055   /// precision issue.
00056   template <typename N1, typename N2>
00057   inline typename std::enable_if<
00058     std::is_integral<N1>::value && std::is_integral<N2>::value, bool>::type
00059   fuzzyEquals(N1 a, N2 b, double UNUSED(tolerance)=1e-5) {
00060     return a == b;
00061   }
00062 
00063 
00064   /// @brief Compare two numbers for >= with a degree of fuzziness
00065   ///
00066   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
00067   template <typename N1, typename N2>
00068   inline typename std::enable_if<
00069     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
00070   fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) {
00071     return a > b || fuzzyEquals(a, b, tolerance);
00072   }
00073 
00074 
00075   /// @brief Compare two floating point numbers for <= with a degree of fuzziness
00076   ///
00077   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
00078   template <typename N1, typename N2>
00079   inline typename std::enable_if<
00080     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
00081   fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) {
00082     return a < b || fuzzyEquals(a, b, tolerance);
00083   }
00084 
00085   //@}
00086 
00087 
00088   /// @name Ranges and intervals
00089   //@{
00090 
00091   /// Represents whether an interval is open (non-inclusive) or closed (inclusive).
00092   ///
00093   /// For example, the interval \f$ [0, \pi) \f$ is closed (an inclusive
00094   /// boundary) at 0, and open (a non-inclusive boundary) at \f$ \pi \f$.
00095   enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };
00096 
00097   /// @brief Determine if @a value is in the range @a low to @a high, for floating point numbers
00098   ///
00099   /// Interval boundary types are defined by @a lowbound and @a highbound.
00100   template <typename N1, typename N2, typename N3>
00101   inline typename std::enable_if<
00102     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00103   inRange(N1 value, N2 low, N3 high,
00104           RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00105     if (lowbound == OPEN && highbound == OPEN) {
00106       return (value > low && value < high);
00107     } else if (lowbound == OPEN && highbound == CLOSED) {
00108       return (value > low && value <= high);
00109     } else if (lowbound == CLOSED && highbound == OPEN) {
00110       return (value >= low && value < high);
00111     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
00112       return (value >= low && value <= high);
00113     }
00114   }
00115 
00116   /// @brief Determine if @a value is in the range @a low to @a high, for floating point numbers
00117   ///
00118   /// Interval boundary types are defined by @a lowbound and @a highbound.
00119   /// Closed intervals are compared fuzzily.
00120   template <typename N1, typename N2, typename N3>
00121   inline typename std::enable_if<
00122     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00123   fuzzyInRange(N1 value, N2 low, N3 high,
00124                RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00125     if (lowbound == OPEN && highbound == OPEN) {
00126       return (value > low && value < high);
00127     } else if (lowbound == OPEN && highbound == CLOSED) {
00128       return (value > low && fuzzyLessEquals(value, high));
00129     } else if (lowbound == CLOSED && highbound == OPEN) {
00130       return (fuzzyGtrEquals(value, low) && value < high);
00131     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
00132       return (fuzzyGtrEquals(value, low) && fuzzyLessEquals(value, high));
00133     }
00134   }
00135 
00136   /// Alternative version of inRange which accepts a pair for the range arguments.
00137   template <typename N1, typename N2, typename N3>
00138   inline typename std::enable_if<
00139     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00140   inRange(N1 value, pair<N2, N3> lowhigh,
00141           RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00142     return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
00143   }
00144 
00145 
00146   // Alternative forms, with snake_case names and boundary types in names rather than as args -- from MCUtils
00147 
00148   /// @brief Boolean function to determine if @a value is within the given range
00149   ///
00150   /// @note The interval is closed (inclusive) at the low end, and open (exclusive) at the high end.
00151   template <typename N1, typename N2, typename N3>
00152   inline typename std::enable_if<
00153     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00154   in_range(N1 val, N2 low, N3 high) {
00155     return inRange(val, low, high, CLOSED, OPEN);
00156   }
00157 
00158   /// @brief Boolean function to determine if @a value is within the given range
00159   ///
00160   /// @note The interval is closed at both ends.
00161   template <typename N1, typename N2, typename N3>
00162   inline typename std::enable_if<
00163     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00164   in_closed_range(N1 val, N2 low, N3 high) {
00165     return inRange(val, low, high, CLOSED, CLOSED);
00166   }
00167 
00168   /// @brief Boolean function to determine if @a value is within the given range
00169   ///
00170   /// @note The interval is open at both ends.
00171   template <typename N1, typename N2, typename N3>
00172   inline typename std::enable_if<
00173     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
00174   in_open_range(N1 val, N2 low, N3 high) {
00175     return inRange(val, low, high, OPEN, OPEN);
00176   }
00177 
00178   /// @todo Add pair-based versions of the named range-boundary functions
00179 
00180   //@}
00181 
00182 
00183   /// @name Miscellaneous numerical helpers
00184   //@{
00185 
00186   /// Named number-type squaring operation.
00187   template <typename NUM>
00188   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
00189   sqr(NUM a) {
00190     return a*a;
00191   }
00192 
00193   /// @brief Named number-type addition in quadrature operation.
00194   ///
00195   /// @note Result has the sqrt operation applied.
00196   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
00197   // template <typename N1, typename N2>
00198   template <typename NUM>
00199   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
00200   //std::common_type<N1, N2>::type
00201   add_quad(NUM a, NUM b) {
00202     return sqrt(a*a + b*b);
00203   }
00204 
00205   /// Named number-type addition in quadrature operation.
00206   ///
00207   /// @note Result has the sqrt operation applied.
00208   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
00209   // template <typename N1, typename N2>
00210   template <typename NUM>
00211   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
00212   //std::common_type<N1, N2, N3>::type
00213   add_quad(NUM a, NUM b, NUM c) {
00214     return sqrt(a*a + b*b + c*c);
00215   }
00216 
00217   /// Return a/b, or @a fail if b = 0
00218   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
00219   inline double safediv(double num, double den, double fail=0.0) {
00220     return (!isZero(den)) ? num/den : fail;
00221   }
00222 
00223   /// A more efficient version of pow for raising numbers to integer powers.
00224   template <typename NUM>
00225   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
00226   intpow(NUM val, unsigned int exp) {
00227     assert(exp >= 0);
00228     if (exp == 0) return (NUM) 1;
00229     else if (exp == 1) return val;
00230     return val * intpow(val, exp-1);
00231   }
00232 
00233   /// Find the sign of a number
00234   template <typename NUM>
00235   inline typename std::enable_if<std::is_arithmetic<NUM>::value, int>::type
00236   sign(NUM val) {
00237     if (isZero(val)) return ZERO;
00238     const int valsign = (val > 0) ? PLUS : MINUS;
00239     return valsign;
00240   }
00241 
00242   //@}
00243 
00244 
00245   /// @name Physics statistical distributions
00246   //@{
00247 
00248   /// @brief CDF for the Breit-Wigner distribution
00249   inline double cdfBW(double x, double mu, double gamma) {
00250     // normalize to (0;1) distribution
00251     const double xn = (x - mu)/gamma;
00252     return std::atan(xn)/M_PI + 0.5;
00253   }
00254 
00255   /// @brief Inverse CDF for the Breit-Wigner distribution
00256   inline double invcdfBW(double p, double mu, double gamma) {
00257     const double xn = std::tan(M_PI*(p-0.5));
00258     return gamma*xn + mu;
00259   }
00260 
00261   //@}
00262 
00263 
00264   /// @name Binning helper functions
00265   //@{
00266 
00267   /// @brief Make a list of @a nbins + 1 values equally spaced between @a start and @a end inclusive.
00268   ///
00269   /// NB. The arg ordering and the meaning of the nbins variable is "histogram-like",
00270   /// as opposed to the Numpy/Matlab version.
00271   inline vector<double> linspace(size_t nbins, double start, double end, bool include_end=true) {
00272     assert(end >= start);
00273     assert(nbins > 0);
00274     vector<double> rtn;
00275     const double interval = (end-start)/static_cast<double>(nbins);
00276     for (size_t i = 0; i < nbins; ++i) {
00277       rtn.push_back(start + i*interval);
00278     }
00279     assert(rtn.size() == nbins);
00280     if (include_end) rtn.push_back(end); //< exact end, not result of n * interval
00281     return rtn;
00282   }
00283 
00284 
00285   /// @brief Make a list of @a nbins + 1 values exponentially spaced between @a start and @a end inclusive.
00286   ///
00287   /// NB. The arg ordering and the meaning of the nbins variable is "histogram-like",
00288   /// as opposed to the Numpy/Matlab version, and the start and end arguments are expressed
00289   /// in "normal" space, rather than as the logarithms of the start/end values as in Numpy/Matlab.
00290   inline vector<double> logspace(size_t nbins, double start, double end, bool include_end=true) {
00291     assert(end >= start);
00292     assert(start > 0);
00293     assert(nbins > 0);
00294     const double logstart = std::log(start);
00295     const double logend = std::log(end);
00296     const vector<double> logvals = linspace(nbins, logstart, logend, false);
00297     assert(logvals.size() == nbins);
00298     vector<double> rtn; rtn.reserve(nbins+1);
00299     rtn.push_back(start); //< exact start, not exp(log(start))
00300     for (size_t i = 1; i < logvals.size(); ++i) {
00301       rtn.push_back(std::exp(logvals[i]));
00302     }
00303     assert(rtn.size() == nbins);
00304     if (include_end) rtn.push_back(end); //< exact end, not exp(n * loginterval)
00305     return rtn;
00306   }
00307 
00308 
00309   /// @brief Make a list of @a nbins + 1 values spaced for equal area
00310   /// Breit-Wigner binning between @a start and @a end inclusive. @a
00311   /// mu and @a gamma are the Breit-Wigner parameters.
00312   ///
00313   /// @note The arg ordering and the meaning of the nbins variable is "histogram-like",
00314   /// as opposed to the Numpy/Matlab version, and the start and end arguments are expressed
00315   /// in "normal" space.
00316   inline vector<double> bwspace(size_t nbins, double start, double end, double mu, double gamma) {
00317     assert(end >= start);
00318     assert(nbins > 0);
00319     const double pmin = cdfBW(start, mu, gamma);
00320     const double pmax = cdfBW(end,   mu, gamma);
00321     const vector<double> edges = linspace(nbins, pmin, pmax);
00322     assert(edges.size() == nbins+1);
00323     vector<double> rtn;
00324     for (double edge : edges) {
00325       rtn.push_back(invcdfBW(edge, mu, gamma));
00326     }
00327     assert(rtn.size() == nbins+1);
00328     return rtn;
00329   }
00330 
00331 
00332   /// @brief Return the bin index of the given value, @a val, given a vector of bin edges
00333   ///
00334   /// @note The @a binedges vector must be sorted
00335   /// @todo Use std::common_type<NUM1, NUM2>::type x = val; ?
00336   template <typename NUM1, typename NUM2>
00337   inline typename std::enable_if<std::is_arithmetic<NUM1>::value && std::is_floating_point<NUM2>::value, int>::type
00338     binIndex(NUM1 val, const vector<NUM2>& binedges, bool allow_overflow=false) {
00339     if (val < binedges.front()) return -1; ///< Below/out of histo range
00340     if (val >= binedges.back()) return allow_overflow ? int(binedges.size())-1 : -1; ///< Above/out of histo range
00341     return std::distance(binedges.begin(), --std::upper_bound(binedges.begin(), binedges.end(), val));
00342     //
00343     // int index = -1;
00344     // for (size_t i = 1; i < binedges.size(); ++i) {
00345     //   if (val < binedges[i]) {
00346     //     index = i-1;
00347     //     break;
00348     //   }
00349     // }
00350     // assert(inRange(index, -1, int(binedges.size())-1));
00351     // return index;
00352   }
00353 
00354   //@}
00355 
00356 
00357   /// @name Discrete statistics functions
00358   //@{
00359 
00360   /// Calculate the median of a sample
00361   /// @todo Support multiple container types via SFINAE
00362   template <typename NUM>
00363   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
00364   median(const vector<NUM>& sample) {
00365     if (sample.empty()) throw RangeError("Can't compute median of an empty set");
00366     vector<NUM> tmp = sample;
00367     std::sort(tmp.begin(), tmp.end());
00368     const size_t imid = tmp.size()/2; // len1->idx0, len2->idx1, len3->idx1, len4->idx2, ...
00369     if (sample.size() % 2 == 0) return (tmp.at(imid-1) + tmp.at(imid)) / 2.0;
00370     else return tmp.at(imid);
00371   }
00372 
00373 
00374   /// Calculate the mean of a sample
00375   /// @todo Support multiple container types via SFINAE
00376   template <typename NUM>
00377   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00378   mean(const vector<NUM>& sample) {
00379     if (sample.empty()) throw RangeError("Can't compute mean of an empty set");
00380     double mean = 0.0;
00381     for (size_t i = 0; i < sample.size(); ++i) {
00382       mean += sample[i];
00383     }
00384     return mean/sample.size();
00385   }
00386 
00387   // Calculate the error on the mean, assuming Poissonian errors
00388   /// @todo Support multiple container types via SFINAE
00389   template <typename NUM>
00390   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00391   mean_err(const vector<NUM>& sample) {
00392     if (sample.empty()) throw RangeError("Can't compute mean_err of an empty set");
00393     double mean_e = 0.0;
00394     for (size_t i = 0; i < sample.size(); ++i) {
00395       mean_e += sqrt(sample[i]);
00396     }
00397     return mean_e/sample.size();
00398   }
00399 
00400 
00401   /// Calculate the covariance (variance) between two samples
00402   /// @todo Support multiple container types via SFINAE
00403   template <typename NUM>
00404   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00405   covariance(const vector<NUM>& sample1, const vector<NUM>& sample2) {
00406     if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance of an empty set");
00407     if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance calculation");
00408     const double mean1 = mean(sample1);
00409     const double mean2 = mean(sample2);
00410     const size_t N = sample1.size();
00411     double cov = 0.0;
00412     for (size_t i = 0; i < N; i++) {
00413       const double cov_i = (sample1[i] - mean1)*(sample2[i] - mean2);
00414       cov += cov_i;
00415     }
00416     if (N > 1) return cov/(N-1);
00417     else return 0.0;
00418   }
00419 
00420   /// Calculate the error on the covariance (variance) of two samples, assuming poissonian errors
00421   /// @todo Support multiple container types via SFINAE
00422   template <typename NUM>
00423   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00424   covariance_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
00425     if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance_err of an empty set");
00426     if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance_err calculation");
00427     const double mean1 = mean(sample1);
00428     const double mean2 = mean(sample2);
00429     const double mean1_e = mean_err(sample1);
00430     const double mean2_e = mean_err(sample2);
00431     const size_t N = sample1.size();
00432     double cov_e = 0.0;
00433     for (size_t i = 0; i < N; i++) {
00434       const double cov_i = (sqrt(sample1[i]) - mean1_e)*(sample2[i] - mean2) +
00435         (sample1[i] - mean1)*(sqrt(sample2[i]) - mean2_e);
00436       cov_e += cov_i;
00437     }
00438     if (N > 1) return cov_e/(N-1);
00439     else return 0.0;
00440   }
00441 
00442 
00443   /// Calculate the correlation strength between two samples
00444   /// @todo Support multiple container types via SFINAE
00445   template <typename NUM>
00446   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00447   correlation(const vector<NUM>& sample1, const vector<NUM>& sample2) {
00448     const double cov = covariance(sample1, sample2);
00449     const double var1 = covariance(sample1, sample1);
00450     const double var2 = covariance(sample2, sample2);
00451     const double correlation = cov/sqrt(var1*var2);
00452     const double corr_strength = correlation*sqrt(var2/var1);
00453     return corr_strength;
00454   }
00455 
00456   /// Calculate the error of the correlation strength between two samples assuming Poissonian errors
00457   /// @todo Support multiple container types via SFINAE
00458   template <typename NUM>
00459   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
00460   correlation_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
00461     const double cov = covariance(sample1, sample2);
00462     const double var1 = covariance(sample1, sample1);
00463     const double var2 = covariance(sample2, sample2);
00464     const double cov_e = covariance_err(sample1, sample2);
00465     const double var1_e = covariance_err(sample1, sample1);
00466     const double var2_e = covariance_err(sample2, sample2);
00467 
00468     // Calculate the correlation
00469     const double correlation = cov/sqrt(var1*var2);
00470     // Calculate the error on the correlation
00471     const double correlation_err = cov_e/sqrt(var1*var2) -
00472       cov/(2*pow(3./2., var1*var2)) * (var1_e * var2 + var1 * var2_e);
00473 
00474     // Calculate the error on the correlation strength
00475     const double corr_strength_err = correlation_err*sqrt(var2/var1) +
00476       correlation/(2*sqrt(var2/var1)) * (var2_e/var1 - var2*var1_e/pow(2, var2));
00477 
00478     return corr_strength_err;
00479   }
00480 
00481   //@}
00482 
00483 
00484   /// @name Angle range mappings
00485   //@{
00486 
00487   /// @brief Reduce any number to the range [-2PI, 2PI]
00488   ///
00489   /// Achieved by repeated addition or subtraction of 2PI as required. Used to
00490   /// normalise angular measures.
00491   inline double _mapAngleM2PITo2Pi(double angle) {
00492     double rtn = fmod(angle, TWOPI);
00493     if (isZero(rtn)) return 0;
00494     assert(rtn >= -TWOPI && rtn <= TWOPI);
00495     return rtn;
00496   }
00497 
00498   /// Map an angle into the range (-PI, PI].
00499   inline double mapAngleMPiToPi(double angle) {
00500     double rtn = _mapAngleM2PITo2Pi(angle);
00501     if (isZero(rtn)) return 0;
00502     if (rtn > PI) rtn -= TWOPI;
00503     if (rtn <= -PI) rtn += TWOPI;
00504     assert(rtn > -PI && rtn <= PI);
00505     return rtn;
00506   }
00507 
00508   /// Map an angle into the range [0, 2PI).
00509   inline double mapAngle0To2Pi(double angle) {
00510     double rtn = _mapAngleM2PITo2Pi(angle);
00511     if (isZero(rtn)) return 0;
00512     if (rtn < 0) rtn += TWOPI;
00513     if (rtn == TWOPI) rtn = 0;
00514     assert(rtn >= 0 && rtn < TWOPI);
00515     return rtn;
00516   }
00517 
00518   /// Map an angle into the range [0, PI].
00519   inline double mapAngle0ToPi(double angle) {
00520     double rtn = fabs(mapAngleMPiToPi(angle));
00521     if (isZero(rtn)) return 0;
00522     assert(rtn > 0 && rtn <= PI);
00523     return rtn;
00524   }
00525 
00526   /// Map an angle into the enum-specified range.
00527   inline double mapAngle(double angle, PhiMapping mapping) {
00528     switch (mapping) {
00529     case MINUSPI_PLUSPI:
00530       return mapAngleMPiToPi(angle);
00531     case ZERO_2PI:
00532       return mapAngle0To2Pi(angle);
00533     case ZERO_PI:
00534       return mapAngle0To2Pi(angle);
00535     default:
00536       throw Rivet::UserError("The specified phi mapping scheme is not implemented");
00537     }
00538   }
00539 
00540   //@}
00541 
00542 
00543   /// @name Phase space measure helpers
00544   //@{
00545 
00546   /// @brief Calculate the difference between two angles in radians
00547   ///
00548   /// Returns in the range [0, PI].
00549   inline double deltaPhi(double phi1, double phi2) {
00550     return mapAngle0ToPi(phi1 - phi2);
00551   }
00552 
00553   /// Calculate the abs difference between two pseudorapidities
00554   ///
00555   /// @note Just a cosmetic name for analysis code clarity.
00556   inline double deltaEta(double eta1, double eta2) {
00557     return fabs(eta1 - eta2);
00558   }
00559 
00560   /// Calculate the abs difference between two rapidities
00561   ///
00562   /// @note Just a cosmetic name for analysis code clarity.
00563   inline double deltaRap(double y1, double y2) {
00564     return fabs(y1 - y2);
00565   }
00566 
00567   /// Calculate the distance between two points in 2D rapidity-azimuthal
00568   /// ("\f$ \eta-\phi \f$") space. The phi values are given in radians.
00569   inline double deltaR(double rap1, double phi1, double rap2, double phi2) {
00570     const double dphi = deltaPhi(phi1, phi2);
00571     return sqrt( sqr(rap1-rap2) + sqr(dphi) );
00572   }
00573 
00574   /// Calculate a rapidity value from the supplied energy @a E and longitudinal momentum @a pz.
00575   inline double rapidity(double E, double pz) {
00576     if (isZero(E - pz)) {
00577       throw std::runtime_error("Divergent positive rapidity");
00578       return MAXDOUBLE;
00579     }
00580     if (isZero(E + pz)) {
00581       throw std::runtime_error("Divergent negative rapidity");
00582       return -MAXDOUBLE;
00583     }
00584     return 0.5*log((E+pz)/(E-pz));
00585   }
00586 
00587   //@}
00588 
00589 
00590 }
00591 
00592 
00593 #endif