Skip to content
Merged
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
1 change: 1 addition & 0 deletions hist/histv7/inc/ROOT/RAxes.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public:
const std::vector<RAxisVariant> &Get() const { return fAxes; }

friend bool operator==(const RAxes &lhs, const RAxes &rhs) { return lhs.fAxes == rhs.fAxes; }
friend bool operator!=(const RAxes &lhs, const RAxes &rhs) { return !(lhs == rhs); }

/// Compute the total number of bins for all axes.
///
Expand Down
54 changes: 53 additions & 1 deletion hist/histv7/inc/ROOT/RHistEngine.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,26 @@ public:
{
}

// Copy constructor and assignment operator are deleted to avoid surprises.
/// The copy constructor is deleted.
///
/// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
/// explicitly call Clone().
RHistEngine(const RHistEngine<BinContentType> &) = delete;
/// Efficiently move construct a histogram engine.
///
/// After this operation, the moved-from object is invalid.
RHistEngine(RHistEngine<BinContentType> &&) = default;

/// The copy assignment operator is deleted.
///
/// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
/// explicitly call Clone().
RHistEngine<BinContentType> &operator=(const RHistEngine<BinContentType> &) = delete;
/// Efficiently move a histogram engine.
///
/// After this operation, the moved-from object is invalid.
RHistEngine<BinContentType> &operator=(RHistEngine<BinContentType> &&) = default;

~RHistEngine() = default;

const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); }
Expand Down Expand Up @@ -153,6 +168,43 @@ public:
return GetBinContent(indices);
}

/// Add all bin contents of another histogram.
///
/// Throws an exception if the axes configurations are not identical.
///
/// \param[in] other another histogram
void Add(const RHistEngine<BinContentType> &other)
{
if (fAxes != other.fAxes) {
throw std::invalid_argument("axes configurations not identical in Add");
}
for (std::size_t i = 0; i < fBinContents.size(); i++) {
fBinContents[i] += other.fBinContents[i];
}
}

/// Clear all bin contents.
void Clear()
{
for (std::size_t i = 0; i < fBinContents.size(); i++) {
fBinContents[i] = {};
}
}

/// Clone this histogram engine.
///
/// Copying all bin contents can be an expensive operation, depending on the number of bins.
///
/// \return the cloned object
RHistEngine<BinContentType> Clone() const
{
RHistEngine<BinContentType> h(fAxes.Get());
for (std::size_t i = 0; i < fBinContents.size(); i++) {
h.fBinContents[i] = fBinContents[i];
}
return h;
}

/// Whether this histogram engine type supported weighted filling.
static constexpr bool SupportsWeightedFilling = std::is_floating_point_v<BinContentType>;

Expand Down
98 changes: 98 additions & 0 deletions hist/histv7/test/hist_engine.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,104 @@ TEST(RHistEngine, GetBinContentNotFound)
EXPECT_THROW(engine.GetBinContent(Bins), std::invalid_argument);
}

TEST(RHistEngine, Add)
{
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, 0, Bins);
RHistEngine<int> engineA({axis});
RHistEngine<int> engineB({axis});
RHistEngine<int> engineC({axis});

engineA.Fill(-100);
for (std::size_t i = 0; i < Bins; i++) {
engineA.Fill(i);
engineA.Fill(i);
engineB.Fill(i);
}
engineB.Fill(100);

engineC.Add(engineA);
engineC.Add(engineB);

engineA.Add(engineB);

EXPECT_EQ(engineA.GetBinContent(RBinIndex::Underflow()), 1);
EXPECT_EQ(engineB.GetBinContent(RBinIndex::Underflow()), 0);
EXPECT_EQ(engineC.GetBinContent(RBinIndex::Underflow()), 1);
for (auto index : axis.GetNormalRange()) {
EXPECT_EQ(engineA.GetBinContent(index), 3);
EXPECT_EQ(engineB.GetBinContent(index), 1);
EXPECT_EQ(engineC.GetBinContent(index), 3);
}
EXPECT_EQ(engineA.GetBinContent(RBinIndex::Overflow()), 1);
EXPECT_EQ(engineB.GetBinContent(RBinIndex::Overflow()), 1);
EXPECT_EQ(engineC.GetBinContent(RBinIndex::Overflow()), 1);
}

TEST(RHistEngine, AddDifferent)
{
// The equality operators of RAxes and the axis objects are already unit-tested separately, so here we only check one
// case with different the number of bins.
RHistEngine<int> engineA(10, 0, 1);
RHistEngine<int> engineB(20, 0, 1);

EXPECT_THROW(engineA.Add(engineB), std::invalid_argument);
}

TEST(RHistEngine, Clear)
{
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, 0, Bins);
RHistEngine<int> engine({axis});

engine.Fill(-100);
for (std::size_t i = 0; i < Bins; i++) {
engine.Fill(i);
}
engine.Fill(100);

engine.Clear();

EXPECT_EQ(engine.GetBinContent(RBinIndex::Underflow()), 0);
for (auto index : axis.GetNormalRange()) {
EXPECT_EQ(engine.GetBinContent(index), 0);
}
EXPECT_EQ(engine.GetBinContent(RBinIndex::Overflow()), 0);
}

TEST(RHistEngine, Clone)
{
static constexpr std::size_t Bins = 20;
const RRegularAxis axis(Bins, 0, Bins);
RHistEngine<int> engineA({axis});

engineA.Fill(-100);
for (std::size_t i = 0; i < Bins; i++) {
engineA.Fill(i);
}
engineA.Fill(100);

RHistEngine<int> engineB = engineA.Clone();
ASSERT_EQ(engineB.GetNDimensions(), 1);
ASSERT_EQ(engineB.GetTotalNBins(), Bins + 2);

EXPECT_EQ(engineB.GetBinContent(RBinIndex::Underflow()), 1);
for (auto index : axis.GetNormalRange()) {
EXPECT_EQ(engineB.GetBinContent(index), 1);
}
EXPECT_EQ(engineB.GetBinContent(RBinIndex::Overflow()), 1);

// Check that we can continue filling the clone.
for (std::size_t i = 0; i < Bins; i++) {
engineB.Fill(i);
}

for (auto index : axis.GetNormalRange()) {
EXPECT_EQ(engineA.GetBinContent(index), 1);
EXPECT_EQ(engineB.GetBinContent(index), 2);
}
}

TEST(RHistEngine, Fill)
{
static constexpr std::size_t Bins = 20;
Expand Down
Loading