1#ifndef BINNEDOBSERVABLEID_H
2#define BINNEDOBSERVABLEID_H
41static inline double norm_zero(
double x)
noexcept {
42 return (x == 0.0) ? 0.0 : x;
55static inline std::uint64_t bits_norm_zero(
double x)
noexcept {
57 return std::bit_cast<std::uint64_t>(x);
90static constexpr std::size_t kBinnedFlhaInsertPos = 2;
98static constexpr std::size_t kBinnedFlhaPartCount = 6;
106static constexpr long kMaxBinFracDigits = 12;
117static inline long pow10_long(
long n)
noexcept {
142static inline std::string trim_decimal_string(std::string s) {
143 auto pos =
s.find(
'.');
144 if (pos == std::string::npos) {
148 while (!
s.empty() &&
s.back() ==
'0') {
152 if (!
s.empty() &&
s.back() ==
'.') {
181static inline EncodedBin encode_bin_gev(
double x) {
182 if (!std::isfinite(x)) {
183 throw std::runtime_error(
"Bin value must be finite");
188 constexpr long double kScale = 1.0e12L;
190 long double xr =
static_cast<long double>(x);
191 xr = std::round(xr * kScale) / kScale;
193 const bool neg = std::signbit(
static_cast<double>(xr));
194 const long double ax = std::fabs(xr);
196 std::ostringstream oss;
197 oss << std::fixed << std::setprecision(kMaxBinFracDigits) << ax;
199 std::string
s = trim_decimal_string(oss.str());
201 const auto pos =
s.find(
'.');
202 std::string int_str = (pos == std::string::npos) ? s :
s.substr(0, pos);
203 std::string frac_str = (pos == std::string::npos) ?
"" :
s.substr(pos + 1);
205 if (int_str.empty()) {
209 const long int_part = std::stol(int_str);
210 const long frac_part = frac_str.empty() ? 0
L : std::stol(frac_str);
211 const long frac_digits =
static_cast<long>(frac_str.size());
215 return EncodedBin{-int_part, frac_part, frac_digits};
217 if (frac_part != 0) {
218 return EncodedBin{0, -frac_part, frac_digits};
222 return EncodedBin{int_part, frac_part, frac_digits};
241static inline double decode_bin_gev(
long int_part,
long frac_part,
long frac_digits) {
242 if (frac_digits < 0 || frac_digits > kMaxBinFracDigits) {
243 throw std::runtime_error(
"decode_bin_gev: invalid frac_digits");
246 const long scale = pow10_long(frac_digits);
247 if (std::labs(frac_part) >= scale && frac_digits > 0) {
248 throw std::runtime_error(
"decode_bin_gev: frac_part does not fit frac_digits");
251 const bool neg = (int_part < 0) || (frac_part < 0);
252 const long abs_int = std::labs(int_part);
253 const long abs_frac = std::labs(frac_part);
255 long double out =
static_cast<long double>(abs_int);
257 if (frac_digits > 0) {
258 out +=
static_cast<long double>(abs_frac) /
static_cast<long double>(scale);
265 return norm_zero(
static_cast<double>(out));
287 std::pair<double, double>
p;
345 && norm_zero(
p.first) == norm_zero(o.p.first)
346 && norm_zero(
p.second) == norm_zero(o.p.second);
360 const std::uint64_t a = bits_norm_zero(
p.first);
361 const std::uint64_t b = bits_norm_zero(
p.second);
362 const std::uint64_t c = bits_norm_zero(o.p.first);
363 const std::uint64_t d = bits_norm_zero(o.p.second);
365 return std::tie(
s, a, b) < std::tie(o.s, c, d);
386 if (!flha_opt.has_value()) {
387 throw std::runtime_error(
"ObservableId to flha mapping unknown");
390 LhaID unbinned_id = flha_opt.value();
392 const EncodedBin low = encode_bin_gev(this->p.first);
393 const EncodedBin high = encode_bin_gev(this->p.second);
395 std::vector<long> bin_parts{
405 parts.insert(parts.begin() + kBinnedFlhaInsertPos, bin_parts.begin(), bin_parts.end());
428 auto parts =
id.get_parts();
430 if (parts.size() < kBinnedFlhaInsertPos + kBinnedFlhaPartCount) {
431 throw std::runtime_error(
"from_flha: LhaID has not enough parts to contain robust binning");
434 const long low_int = parts.at(kBinnedFlhaInsertPos + 0);
435 const long low_frac = parts.at(kBinnedFlhaInsertPos + 1);
436 const long low_ndigits = parts.at(kBinnedFlhaInsertPos + 2);
437 const long high_int = parts.at(kBinnedFlhaInsertPos + 3);
438 const long high_frac = parts.at(kBinnedFlhaInsertPos + 4);
439 const long high_ndigits = parts.at(kBinnedFlhaInsertPos + 5);
441 parts.erase(parts.begin() + kBinnedFlhaInsertPos,
442 parts.begin() + kBinnedFlhaInsertPos + kBinnedFlhaPartCount);
443 LhaID unbinned_id(parts);
446 if (!obs_opt.has_value()) {
447 throw std::runtime_error(
"from_flha: flha to ObservableId mapping unknown");
451 out.
s = obs_opt.value();
453 decode_bin_gev(low_int, low_frac, low_ndigits),
454 decode_bin_gev(high_int, high_frac, high_ndigits)
469 std::stringstream ss;
470 ss <<
s.
str() <<
" [" <<
p.first <<
", " <<
p.second <<
"]";
495 std::size_t h = std::hash<ObservableId>{}(
id.s);
497 auto mix = [](std::size_t& seed, std::size_t v) {
498 seed ^= v + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
501 mix(h, std::hash<std::uint64_t>{}(bits_norm_zero(
id.p.first)));
502 mix(h, std::hash<std::uint64_t>{}(bits_norm_zero(
id.p.second)));
519 return os <<
id.str();
std::ostream & operator<<(std::ostream &os, BinnedObservableId const &id)
Streams a human-readable binned observable identifier.
High-level mapper for observable names, FLHA ids and parent decays.
static std::optional< ObservableId > from_flha(const LhaID &ext)
Resolve a FLHA id to a dynamic observable id.
static std::optional< LhaID > flha_of(const ObservableId &id)
Return the FLHA id attached to an observable id, if any.
const std::string & str() const
Returns the underlying string.
Mapper for builtin and runtime observable identifiers.
Identifies an observable together with a numerical bin.
BinnedObservableId(ObservableId id, std::pair< double, double > bin)
Constructs a binned observable identifier.
bool operator==(BinnedObservableId const &o) const noexcept
Equality comparison operator.
bool operator<(BinnedObservableId const &o) const noexcept
Strict weak ordering for binned observable identifiers.
LhaID flha() const
Converts this binned observable identifier to its FLHA encoding.
BinnedObservableId(ObservableId id)
Constructs an unbinned observable identifier.
BinnedObservableId(Observables id, std::pair< double, double > bin)
Constructs a binned observable identifier.
static BinnedObservableId from_flha(LhaID const &id)
Reconstructs a binned observable identifier from an FLHA encoding.
BinnedObservableId()=default
Default constructor.
BinnedObservableId(Observables id)
Constructs an unbinned observable identifier.
std::pair< double, double > p
std::string str() const
Returns a human-readable representation of the binned observable.
Integer representation of a decimal bin boundary.
Represents an identifier of a LHA element, possibly containing several sub-ids.
std::vector< long > get_parts() const
Returns the underlying vector of sub-ids.
std::size_t operator()(BinnedObservableId const &id) const noexcept
Computes the hash of a binned observable identifier.