Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions hist/histv7/inc/ROOT/RHist.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ hist.Fill(8.5);
// hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
\endcode

The class is templated on the bin content type. For counting, as in the example above, it may be an integer type such as
`int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their limited
range and must be used with care. For weighted filling, the bin content type must be a floating-point type such as
`float` or `double`, or the special type RBinWithError. Note that `float` has a limited significand precision of 24
bits.
The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such
as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their
limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but
a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited
significand precision of 24 bits.

An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
RAxisVariant:
Expand Down Expand Up @@ -219,8 +219,7 @@ public:

/// Fill an entry into the histogram with a weight.
///
/// This overload is only available for floating-point bin content types (see
/// \ref RHistEngine::SupportsWeightedFilling).
/// This overload is not available for integral bin content types (see \ref RHistEngine::SupportsWeightedFilling).
///
/// \code
/// ROOT::Experimental::RHist<float> hist({/* two dimensions */});
Expand Down Expand Up @@ -257,7 +256,7 @@ public:
/// ROOT::Experimental::RHist<float> hist({/* two dimensions */});
/// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
/// \endcode
/// This is only available for floating-point bin content types (see \ref RHistEngine::SupportsWeightedFilling).
/// This is not available for integral bin content types (see \ref RHistEngine::SupportsWeightedFilling).
///
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
/// discarded.
Expand Down
23 changes: 10 additions & 13 deletions hist/histv7/inc/ROOT/RHistEngine.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include "RAxes.hxx"
#include "RBinIndex.hxx"
#include "RBinWithError.hxx"
#include "RHistUtils.hxx"
#include "RLinearizedIndex.hxx"
#include "RRegularAxis.hxx"
Expand Down Expand Up @@ -37,11 +36,11 @@ hist.Fill(8.5);
// hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
\endcode

The class is templated on the bin content type. For counting, as in the example above, it may be an integer type such as
`int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their limited
range and must be used with care. For weighted filling, the bin content type must be a floating-point type such as
`float` or `double`, or the special type RBinWithError. Note that `float` has a limited significand precision of 24
bits.
The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such
as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their
limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but
a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited
significand precision of 24 bits.

An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
RAxisVariant:
Expand Down Expand Up @@ -209,8 +208,7 @@ public:
}

/// Whether this histogram engine type supports weighted filling.
static constexpr bool SupportsWeightedFilling =
std::is_floating_point_v<BinContentType> || std::is_same_v<BinContentType, RBinWithError>;
static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>;

/// Fill an entry into the histogram.
///
Expand Down Expand Up @@ -246,7 +244,7 @@ public:

/// Fill an entry into the histogram with a weight.
///
/// This overload is only available for floating-point bin content types (see \ref SupportsWeightedFilling).
/// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
///
/// \code
/// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
Expand All @@ -267,7 +265,7 @@ public:
template <typename... A>
void Fill(const std::tuple<A...> &args, RWeight weight)
{
static_assert(SupportsWeightedFilling, "weighted filling is only supported for floating-point bin content types");
static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");

// We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
// be confusing for users.
Expand All @@ -293,7 +291,7 @@ public:
/// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
/// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
/// \endcode
/// This is only available for floating-point bin content types (see \ref SupportsWeightedFilling).
/// This is not available for integral bin content types (see \ref SupportsWeightedFilling).
///
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
/// discarded.
Expand All @@ -309,8 +307,7 @@ public:
{
auto t = std::forward_as_tuple(args...);
if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
static_assert(SupportsWeightedFilling,
"weighted filling is only supported for floating-point bin content types");
static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
static constexpr std::size_t N = sizeof...(A) - 1;
if (N != fAxes.GetNDimensions()) {
throw std::invalid_argument("invalid number of arguments to Fill");
Expand Down
1 change: 1 addition & 0 deletions hist/histv7/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ HIST_ADD_GTEST(hist_hist hist_hist.cxx)
HIST_ADD_GTEST(hist_index hist_index.cxx)
HIST_ADD_GTEST(hist_regular hist_regular.cxx)
HIST_ADD_GTEST(hist_stats hist_stats.cxx)
HIST_ADD_GTEST(hist_user hist_user.cxx)
HIST_ADD_GTEST(hist_variable hist_variable.cxx)

if(NOT DEFINED histv7_standalone)
Expand Down
4 changes: 4 additions & 0 deletions hist/histv7/test/hist_hist.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ TEST(RHist, FillWeight)
hist.Fill(8.5, RWeight(0.8));
hist.Fill(std::make_tuple(9.5), RWeight(0.9));

EXPECT_FLOAT_EQ(hist.GetBinContent(RBinIndex(8)), 0.8);
std::array<RBinIndex, 1> indices = {9};
EXPECT_FLOAT_EQ(hist.GetBinContent(indices), 0.9);

EXPECT_EQ(hist.GetStats().GetNEntries(), 2);
// Cross-checked with TH1
EXPECT_FLOAT_EQ(hist.GetStats().ComputeNEffectiveEntries(), 1.9931034);
Expand Down
121 changes: 121 additions & 0 deletions hist/histv7/test/hist_user.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "hist_test.hxx"

#include <type_traits>

// User-defined bin content type consisting of a single double, but can be much more complicated.
struct User {
double fValue = 0;

User &operator++()
{
fValue++;
return *this;
}

User operator++(int)
{
User old = *this;
operator++();
return old;
}

User &operator+=(double w)
{
fValue += w;
return *this;
}

User &operator+=(const User &rhs)
{
fValue += rhs.fValue;
return *this;
}
};

static_assert(std::is_nothrow_move_constructible_v<RHistEngine<User>>);
static_assert(std::is_nothrow_move_assignable_v<RHistEngine<User>>);

static_assert(std::is_nothrow_move_constructible_v<RHist<User>>);
static_assert(std::is_nothrow_move_assignable_v<RHist<User>>);

TEST(RHistEngineUser, Add)
{
// Addition uses operator+=(const User &)
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, {0, Bins});
RHistEngine<User> engineA({axis});
RHistEngine<User> engineB({axis});

engineA.Fill(8.5);
engineB.Fill(9.5);

engineA.Add(engineB);

EXPECT_EQ(engineA.GetBinContent(RBinIndex(8)).fValue, 1);
EXPECT_EQ(engineA.GetBinContent(RBinIndex(9)).fValue, 1);
}

TEST(RHistEngineUser, Clear)
{
// Clearing assigns default-constructed objects.
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, {0, Bins});
RHistEngine<User> engine({axis});

engine.Fill(8.5);
engine.Fill(9.5);

engine.Clear();

EXPECT_EQ(engine.GetBinContent(RBinIndex(8)).fValue, 0);
EXPECT_EQ(engine.GetBinContent(RBinIndex(9)).fValue, 0);
}

TEST(RHistEngineUser, Clone)
{
// Cloning copy-assigns the objects.
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, {0, Bins});
RHistEngine<User> engineA({axis});

engineA.Fill(8.5);

RHistEngine<User> engineB = engineA.Clone();
EXPECT_EQ(engineB.GetBinContent(8).fValue, 1);

// Check that we can continue filling the clone.
engineB.Fill(9.5);

EXPECT_EQ(engineA.GetBinContent(9).fValue, 0);
EXPECT_EQ(engineB.GetBinContent(9).fValue, 1);
}

TEST(RHistEngineUser, Fill)
{
// Unweighted filling uses operator++(int)
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, {0, Bins});
RHistEngine<User> engine({axis});

engine.Fill(8.5);
engine.Fill(std::make_tuple(9.5));

EXPECT_EQ(engine.GetBinContent(RBinIndex(8)).fValue, 1);
std::array<RBinIndex, 1> indices = {9};
EXPECT_EQ(engine.GetBinContent(indices).fValue, 1);
}

TEST(RHistEngineUser, FillWeight)
{
// Weighted filling uses operator+=(double)
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, {0, Bins});
RHistEngine<User> engine({axis});

engine.Fill(8.5, RWeight(0.8));
engine.Fill(std::make_tuple(9.5), RWeight(0.9));

EXPECT_EQ(engine.GetBinContent(RBinIndex(8)).fValue, 0.8);
std::array<RBinIndex, 1> indices = {9};
EXPECT_EQ(engine.GetBinContent(indices).fValue, 0.9);
}
Loading