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/headers.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(histv7_headers
ROOT/RAxes.hxx
ROOT/RBinIndex.hxx
ROOT/RBinIndexRange.hxx
ROOT/RLinearizedIndex.hxx
ROOT/RRegularAxis.hxx
ROOT/RVariableBinAxis.hxx
Expand Down
147 changes: 147 additions & 0 deletions hist/histv7/inc/ROOT/RBinIndexRange.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/// \file
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

#ifndef ROOT_RBinIndexRange
#define ROOT_RBinIndexRange

#include "RBinIndex.hxx"

#include <cassert>
#include <cstddef>
#include <iterator>

namespace ROOT {
namespace Experimental {

// forward declarations for friend declaration
class RBinIndexRange;
namespace Internal {
RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::size_t nNormalBins);
} // namespace Internal

/**
A range of bin indices \f$[fBegin, fEnd)\f$.

The interface allows convenient iteration over RBinIndex. If included, RBinIndex::Underflow() is encountered before the
normal bins and RBinIndex::Overflow() is the last value.

\code
ROOT::Experimental::RRegularAxis axis(10, 0, 1);
for (auto index : axis.GetNormalRange(2, 5)) {
// Will iterate over [2, 3, 4]
}
for (auto index : axis.GetFullRange()) {
// Will iterate over all bins, starting with the underflow and ending with the overflow bin
}
\endcode

\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is
welcome!
*/
class RBinIndexRange final {
friend RBinIndexRange Internal::CreateBinIndexRange(RBinIndex, RBinIndex, std::size_t);

/// The begin of the range (inclusive)
RBinIndex fBegin;
/// The end of the range (exclusive)
RBinIndex fEnd;
/// The number of normal bins, after which iteration advances to RBinIndex::Overflow()
std::size_t fNNormalBins = 0;

public:
/// Construct an invalid bin index range.
RBinIndexRange() = default;

RBinIndex GetBegin() const { return fBegin; }
RBinIndex GetEnd() const { return fEnd; }
// fNNormalBins is not exposed because it might be confusing for partial ranges.

friend bool operator==(const RBinIndexRange &lhs, const RBinIndexRange &rhs)
{
return lhs.fBegin == rhs.fBegin && lhs.fEnd == rhs.fEnd && lhs.fNNormalBins == rhs.fNNormalBins;
}

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

/// %Iterator over RBinIndex.
class Iterator final {
/// The current bin index
RBinIndex fIndex;
/// The number of normal bins, after which iteration advances to RBinIndex::Overflow()
std::size_t fNNormalBins = 0;

public:
using difference_type = std::ptrdiff_t;
using value_type = RBinIndex;
using pointer = const RBinIndex *;
using reference = RBinIndex;
using iterator_category = std::input_iterator_tag;

Iterator() = default;
Iterator(RBinIndex index, std::size_t nNormalBins) : fIndex(index), fNNormalBins(nNormalBins) {}

Iterator &operator++()
{
if (fIndex.IsUnderflow()) {
fIndex = 0;
} else if (fIndex.IsOverflow()) {
fIndex = RBinIndex();
} else if (fIndex.IsInvalid()) {
// This should never happen! In the worst case, when built with NDEBUG, the iterator stays at Invalid.
assert(0);
} else {
fIndex++;
if (fIndex.GetIndex() == fNNormalBins) {
fIndex = RBinIndex::Overflow();
}
}
return *this;
}
Iterator operator++(int)
{
Iterator old = *this;
operator++();
return old;
}

RBinIndex operator*() const { return fIndex; }
const RBinIndex *operator->() const { return &fIndex; }

friend bool operator==(const Iterator &lhs, const Iterator &rhs)
{
return lhs.fIndex == rhs.fIndex && lhs.fNNormalBins == rhs.fNNormalBins;
}
friend bool operator!=(const Iterator &lhs, const Iterator &rhs) { return !(lhs == rhs); }
};

Iterator begin() const { return Iterator(fBegin, fNNormalBins); }
Iterator end() const { return Iterator(fEnd, fNNormalBins); }
};

namespace Internal {

/// %Internal function to create RBinIndexRange.
///
/// Users are strongly advised to create bin index ranges via the respective axis types, for example with
/// \ref RRegularAxis::GetNormalRange(RBinIndex, RBinIndex) const "RRegularAxis::GetNormalRange(RBinIndex, RBinIndex)"
/// or RRegularAxis::GetFullRange().
///
/// \param[in] begin the begin of the bin index range (inclusive)
/// \param[in] end the end of the bin index range (exclusive)
/// \param[in] nNormalBins the number of normal bins, after which iteration advances to RBinIndex::Overflow()
RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::size_t nNormalBins)
{
RBinIndexRange range;
range.fBegin = begin;
range.fEnd = end;
range.fNNormalBins = nNormalBins;
return range;
}

} // namespace Internal

} // namespace Experimental
} // namespace ROOT

#endif
45 changes: 45 additions & 0 deletions hist/histv7/inc/ROOT/RRegularAxis.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define ROOT_RRegularAxis

#include "RBinIndex.hxx"
#include "RBinIndexRange.hxx"
#include "RLinearizedIndex.hxx"

#include <cassert>
Expand Down Expand Up @@ -120,6 +121,50 @@ public:
return {bin, bin < fNNormalBins};
}

/// Get the range of all normal bins.
///
/// \return the bin index range from the first to the last normal bin, inclusive
RBinIndexRange GetNormalRange() const
{
return Internal::CreateBinIndexRange(RBinIndex(0), RBinIndex(fNNormalBins), 0);
}

/// Get a range of normal bins.
///
/// \param[in] begin the begin of the bin index range (inclusive), must be normal
/// \param[in] end the end of the bin index range (exclusive), must be normal and >= begin
/// \return a bin index range \f$[begin, end)\f$
RBinIndexRange GetNormalRange(RBinIndex begin, RBinIndex end) const
{
if (!begin.IsNormal()) {
throw std::invalid_argument("begin must be a normal bin");
}
if (begin.GetIndex() >= fNNormalBins) {
throw std::invalid_argument("begin must be inside the axis");
}
if (!end.IsNormal()) {
throw std::invalid_argument("end must be a normal bin");
}
if (end.GetIndex() > fNNormalBins) {
throw std::invalid_argument("end must be inside or past the axis");
}
if (!(end >= begin)) {
throw std::invalid_argument("end must be >= begin");
}
return Internal::CreateBinIndexRange(begin, end, 0);
}

/// Get the full range of all bins.
///
/// This includes underflow and overflow bins, if enabled.
///
/// \return the bin index range of all bins
RBinIndexRange GetFullRange() const
{
return fEnableFlowBins ? Internal::CreateBinIndexRange(RBinIndex::Underflow(), RBinIndex(), fNNormalBins)
: GetNormalRange();
}

/// ROOT Streamer function to throw when trying to store an object of this class.
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RRegularAxis"); }
};
Expand Down
45 changes: 45 additions & 0 deletions hist/histv7/inc/ROOT/RVariableBinAxis.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define ROOT_RVariableBinAxis

#include "RBinIndex.hxx"
#include "RBinIndexRange.hxx"
#include "RLinearizedIndex.hxx"

#include <cassert>
Expand Down Expand Up @@ -121,6 +122,50 @@ public:
return {bin, bin < fBinEdges.size() - 1};
}

/// Get the range of all normal bins.
///
/// \return the bin index range from the first to the last normal bin, inclusive
RBinIndexRange GetNormalRange() const
{
return Internal::CreateBinIndexRange(RBinIndex(0), RBinIndex(fBinEdges.size() - 1), 0);
}

/// Get a range of normal bins.
///
/// \param[in] begin the begin of the bin index range (inclusive), must be normal
/// \param[in] end the end of the bin index range (exclusive), must be normal and >= begin
/// \return a bin index range \f$[begin, end)\f$
RBinIndexRange GetNormalRange(RBinIndex begin, RBinIndex end) const
{
if (!begin.IsNormal()) {
throw std::invalid_argument("begin must be a normal bin");
}
if (begin.GetIndex() >= fBinEdges.size() - 1) {
throw std::invalid_argument("begin must be inside the axis");
}
if (!end.IsNormal()) {
throw std::invalid_argument("end must be a normal bin");
}
if (end.GetIndex() > fBinEdges.size() - 1) {
throw std::invalid_argument("end must be inside or past the axis");
}
if (!(end >= begin)) {
throw std::invalid_argument("end must be >= begin");
}
return Internal::CreateBinIndexRange(begin, end, 0);
}

/// Get the full range of all bins.
///
/// This includes underflow and overflow bins, if enabled.
///
/// \return the bin index range of all bins
RBinIndexRange GetFullRange() const
{
return fEnableFlowBins ? Internal::CreateBinIndexRange(RBinIndex::Underflow(), RBinIndex(), fBinEdges.size() - 1)
: GetNormalRange();
}

/// ROOT Streamer function to throw when trying to store an object of this class.
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RVariableBinAxis"); }
};
Expand Down
56 changes: 56 additions & 0 deletions hist/histv7/test/hist_index.cxx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "hist_test.hxx"

#include <iterator>
#include <vector>

TEST(RBinIndex, Constructor)
{
const RBinIndex invalid;
Expand Down Expand Up @@ -147,3 +150,56 @@ TEST(RBinIndex, Relation)
EXPECT_FALSE(underflow > overflow);
EXPECT_FALSE(underflow >= overflow);
}

using ROOT::Experimental::Internal::CreateBinIndexRange;

TEST(RBinIndexRange, ConstructorCreate)
{
const RBinIndexRange invalid;
EXPECT_TRUE(invalid.GetBegin().IsInvalid());
EXPECT_TRUE(invalid.GetEnd().IsInvalid());

const auto index0 = RBinIndex(0);
const auto range0 = CreateBinIndexRange(index0, index0, 0);
EXPECT_EQ(range0.GetBegin(), index0);
EXPECT_EQ(range0.GetEnd(), index0);

const auto range01 = CreateBinIndexRange(index0, RBinIndex(1), 1);
EXPECT_EQ(range01.GetBegin(), index0);
EXPECT_EQ(range01.GetEnd(), RBinIndex(1));
}

TEST(RBinIndexRange, Empty)
{
const auto index0 = RBinIndex(0);
const auto empty = CreateBinIndexRange(index0, index0, 0);
EXPECT_EQ(empty.begin(), empty.end());
EXPECT_EQ(std::distance(empty.begin(), empty.end()), 0);
}

TEST(RBinIndexRange, Normal)
{
const auto index0 = RBinIndex(0);
const auto range01 = CreateBinIndexRange(index0, RBinIndex(1), 0);
EXPECT_EQ(std::distance(range01.begin(), range01.end()), 1);
auto range01It = range01.begin();
EXPECT_TRUE(range01It->IsNormal());
EXPECT_EQ(*range01It, index0);
range01It++;
EXPECT_EQ(range01It, range01.end());
}

TEST(RBinIndexRange, Full)
{
const auto underflow = RBinIndex::Underflow();
const RBinIndex invalid;
const auto full = CreateBinIndexRange(underflow, invalid, /*nNormalBins=*/10);
EXPECT_EQ(full.GetBegin(), underflow);
EXPECT_EQ(full.GetEnd(), invalid);
EXPECT_EQ(std::distance(full.begin(), full.end()), 12);

const std::vector binIndexes(full.begin(), full.end());
ASSERT_EQ(binIndexes.size(), 12);
EXPECT_TRUE(binIndexes.front().IsUnderflow());
EXPECT_TRUE(binIndexes.back().IsOverflow());
}
Loading
Loading