#include "gtest/gtest.h" #include #include // Implementation of op+= for integral. template std::atomic &atomic_addeq(std::atomic &lhs, INTEGRAL rhs, decltype(std::declval>().fetch_add(INTEGRAL())) * = nullptr) { lhs.fetch_add(rhs); return lhs; } // Implementation of op+= for floats. template std::atomic &atomic_addeq(std::atomic &lhs, FLOAT rhs, typename std::enable_if::value>::type * = nullptr) { auto oldval = lhs.load(std::memory_order_relaxed); auto newval = oldval + rhs; while (lhs.compare_exchange_strong(oldval, newval)) { // oldval is changed to what bin holds at the call to compare_exchange_strong newval = oldval + rhs; } return lhs; } /// \class WrappedAtomic. /// \brief Provides copy constructor for `atomic` and `+=` even for floats. /// /// It provides the operations needed for `Hist` with atomic bin content. template class WrappedAtomic { private: /// The wrapped atomic value. std::atomic fVal; public: /// Value-initialize the atomic. WrappedAtomic(): fVal(T{}) {} /// Copy-construct the atomic. WrappedAtomic(const WrappedAtomic &other): fVal(other.fVal.load(std::memory_order_relaxed)) {} /// Construct the atomic from the underlying type. WrappedAtomic(T other): fVal(other) {} /// Increment operator, as needed by histogram filling. WrappedAtomic &operator+=(T rhs) { atomic_addeq(fVal, rhs); return *this; } /// Implicitly convert to the underlying type. operator T() const { return fVal.load(std::memory_order_relaxed); } }; /** Basic tests for histograms of integral precision using vector storage. */ #include "ROOT/RHist.hxx" using namespace ROOT::Experimental; // Storage using vector> template using atomicvec_t = std::vector>; // A RHistData using vector> as storage. template using content_t = Detail::RHistData, RHistStatContent>; template using uncert_t = Detail::RHistData, RHistStatContent, RHistStatUncertainty>; // Test creation of RHistImpl with atomic precision. TEST(HistAtomicPrecisionTest, Create) { Detail::RHistImpl, RAxisEquidistant> h1I(RAxisEquidistant{100, 0., 1}); Detail::RHistImpl, RAxisEquidistant, RAxisEquidistant> h2C(RAxisEquidistant{100, 0., 1}, RAxisEquidistant{10, -1., 1}); Detail::RHistImpl, RAxisIrregular> h1LLIrr(RAxisIrregular{{0., 0.1, 0.5, 1}}); Detail::RHistImpl, RAxisEquidistant> h1F(RAxisEquidistant{100, 0., 1}); Detail::RHistImpl, RAxisEquidistant> h1D(RAxisEquidistant{100, 0., 1}); } // Test filling of RHistImpl with atomic precision. TEST(HistAtomicPrecisionTest, Fill1Int) { Detail::RHistImpl, RAxisEquidistant> hist(RAxisEquidistant{100, 0., 1}); hist.Fill({0.2222}, 12); EXPECT_EQ(12, hist.GetBinContent({0.2222})); } TEST(HistAtomicPrecisionTest, Fill2LongLong) { Detail::RHistImpl, RAxisEquidistant, RAxisEquidistant> hist(RAxisEquidistant{100, 0., 1}, RAxisEquidistant{10, -1., 1}); hist.Fill({0.1111, -0.2222}, 42ll); EXPECT_EQ(42ll, hist.GetBinContent({0.1111, -0.2222})); } TEST(HistAtomicPrecisionTest, Fill1CharIrr) { Detail::RHistImpl, RAxisIrregular> hist(RAxisIrregular{{0., 0.1, 0.5, 1}}); hist.Fill({0.1111}, 17); EXPECT_EQ(17, hist.GetBinContent({0.1111})); } TEST(HistAtomicPrecisionTest, Fill1Double) { Detail::RHistImpl, RAxisEquidistant> hist(RAxisEquidistant{100, 0., 1}); hist.Fill({0.2222}, 19.); EXPECT_DOUBLE_EQ(19., hist.GetBinContent({0.2222})); } TEST(HistAtomicPrecisionTest, Fill1Float) { Detail::RHistImpl, RAxisEquidistant> hist(RAxisEquidistant{100, 0., 1}); hist.Fill({0.9999}, -9.); EXPECT_FLOAT_EQ(-9., hist.GetBinContent({0.9999})); EXPECT_FLOAT_EQ(9., hist.GetBinUncertainty(hist.GetBinIndex({0.9999}))); }