Skip to content

Commit ff926f7

Browse files
committed
[hist] Implement RBinIndexRange
The interface allows convenient iteration over RBinIndex.
1 parent 009cee2 commit ff926f7

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

hist/histv7/headers.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
set(histv7_headers
22
ROOT/RAxes.hxx
33
ROOT/RBinIndex.hxx
4+
ROOT/RBinIndexRange.hxx
45
ROOT/RLinearizedIndex.hxx
56
ROOT/RRegularAxis.hxx
67
ROOT/RVariableBinAxis.hxx
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/// \file
2+
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
3+
/// is welcome!
4+
5+
#ifndef ROOT_RBinIndexRange
6+
#define ROOT_RBinIndexRange
7+
8+
#include "RBinIndex.hxx"
9+
10+
#include <cassert>
11+
#include <cstddef>
12+
#include <iterator>
13+
14+
namespace ROOT {
15+
namespace Experimental {
16+
17+
// forward declarations for friend declaration
18+
class RBinIndexRange;
19+
namespace Internal {
20+
RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::size_t nNormalBins);
21+
} // namespace Internal
22+
23+
/**
24+
A range of bin indices \f$[fBegin, fEnd)\f$.
25+
26+
The interface allows convenient iteration over RBinIndex. If included, RBinIndex::Underflow() is encountered before the
27+
normal bins and RBinIndex::Overflow() is the last value.
28+
29+
\warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is
30+
welcome!
31+
*/
32+
class RBinIndexRange final {
33+
friend RBinIndexRange Internal::CreateBinIndexRange(RBinIndex, RBinIndex, std::size_t);
34+
35+
/// The begin of the range (inclusive)
36+
RBinIndex fBegin;
37+
/// The end of the range (exclusive)
38+
RBinIndex fEnd;
39+
/// The number of normal bins, after which iteration advances to RBinIndex::Overflow()
40+
std::size_t fNNormalBins = 0;
41+
42+
public:
43+
/// Construct an invalid bin index range.
44+
RBinIndexRange() = default;
45+
46+
RBinIndex GetBegin() const { return fBegin; }
47+
RBinIndex GetEnd() const { return fEnd; }
48+
// fNNormalBins is not exposed because it might be confusing for partial ranges.
49+
50+
friend bool operator==(const RBinIndexRange &lhs, const RBinIndexRange &rhs)
51+
{
52+
return lhs.fBegin == rhs.fBegin && lhs.fEnd == rhs.fEnd && lhs.fNNormalBins == rhs.fNNormalBins;
53+
}
54+
55+
friend bool operator!=(const RBinIndexRange &lhs, const RBinIndexRange &rhs) { return !(lhs == rhs); }
56+
57+
/// Iterator over RBinIndex.
58+
class Iterator final {
59+
/// The current bin index
60+
RBinIndex fIndex;
61+
/// The number of normal bins, after which iteration advances to RBinIndex::Overflow()
62+
std::size_t fNNormalBins = 0;
63+
64+
public:
65+
using difference_type = std::ptrdiff_t;
66+
using value_type = RBinIndex;
67+
using pointer = const RBinIndex *;
68+
using reference = RBinIndex;
69+
using iterator_category = std::input_iterator_tag;
70+
71+
Iterator() = default;
72+
Iterator(RBinIndex index, std::size_t nNormalBins) : fIndex(index), fNNormalBins(nNormalBins) {}
73+
74+
Iterator &operator++()
75+
{
76+
if (fIndex.IsUnderflow()) {
77+
fIndex = 0;
78+
} else if (fIndex.IsOverflow()) {
79+
fIndex = RBinIndex();
80+
} else if (fIndex.IsInvalid()) {
81+
// This should never happen! In the worst case, when built with NDEBUG, the iterator stays at Invalid.
82+
assert(0);
83+
} else {
84+
fIndex++;
85+
if (fIndex.GetIndex() == fNNormalBins) {
86+
fIndex = RBinIndex::Overflow();
87+
}
88+
}
89+
return *this;
90+
}
91+
Iterator operator++(int)
92+
{
93+
Iterator old = *this;
94+
operator++();
95+
return old;
96+
}
97+
98+
RBinIndex operator*() const { return fIndex; }
99+
const RBinIndex *operator->() const { return &fIndex; }
100+
101+
friend bool operator==(const Iterator &lhs, const Iterator &rhs)
102+
{
103+
return lhs.fIndex == rhs.fIndex && lhs.fNNormalBins == rhs.fNNormalBins;
104+
}
105+
friend bool operator!=(const Iterator &lhs, const Iterator &rhs) { return !(lhs == rhs); }
106+
};
107+
108+
Iterator begin() const { return Iterator(fBegin, fNNormalBins); }
109+
Iterator end() const { return Iterator(fEnd, fNNormalBins); }
110+
};
111+
112+
namespace Internal {
113+
114+
/// Internal function to create RBinIndexRange.
115+
///
116+
/// \param[in] begin the begin of the bin index range (inclusive)
117+
/// \param[in] end the end of the bin index range (exclusive)
118+
/// \param[in] nNormalBins the number of normal bins, after which iteration advances to RBinIndex::Overflow()
119+
RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::size_t nNormalBins)
120+
{
121+
RBinIndexRange range;
122+
range.fBegin = begin;
123+
range.fEnd = end;
124+
range.fNNormalBins = nNormalBins;
125+
return range;
126+
}
127+
128+
} // namespace Internal
129+
130+
} // namespace Experimental
131+
} // namespace ROOT
132+
133+
#endif

hist/histv7/test/hist_index.cxx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "hist_test.hxx"
22

3+
#include <iterator>
4+
#include <vector>
5+
36
TEST(RBinIndex, Constructor)
47
{
58
const RBinIndex invalid;
@@ -147,3 +150,56 @@ TEST(RBinIndex, Relation)
147150
EXPECT_FALSE(underflow > overflow);
148151
EXPECT_FALSE(underflow >= overflow);
149152
}
153+
154+
using ROOT::Experimental::Internal::CreateBinIndexRange;
155+
156+
TEST(RBinIndexRange, ConstructorCreate)
157+
{
158+
const RBinIndexRange invalid;
159+
EXPECT_TRUE(invalid.GetBegin().IsInvalid());
160+
EXPECT_TRUE(invalid.GetEnd().IsInvalid());
161+
162+
const auto index0 = RBinIndex(0);
163+
const auto range0 = CreateBinIndexRange(index0, index0, 0);
164+
EXPECT_EQ(range0.GetBegin(), index0);
165+
EXPECT_EQ(range0.GetEnd(), index0);
166+
167+
const auto range01 = CreateBinIndexRange(index0, RBinIndex(1), 1);
168+
EXPECT_EQ(range01.GetBegin(), index0);
169+
EXPECT_EQ(range01.GetEnd(), RBinIndex(1));
170+
}
171+
172+
TEST(RBinIndexRange, Empty)
173+
{
174+
const auto index0 = RBinIndex(0);
175+
const auto empty = CreateBinIndexRange(index0, index0, 0);
176+
EXPECT_EQ(empty.begin(), empty.end());
177+
EXPECT_EQ(std::distance(empty.begin(), empty.end()), 0);
178+
}
179+
180+
TEST(RBinIndexRange, Normal)
181+
{
182+
const auto index0 = RBinIndex(0);
183+
const auto range01 = CreateBinIndexRange(index0, RBinIndex(1), 0);
184+
EXPECT_EQ(std::distance(range01.begin(), range01.end()), 1);
185+
auto range01It = range01.begin();
186+
EXPECT_TRUE(range01It->IsNormal());
187+
EXPECT_EQ(*range01It, index0);
188+
range01It++;
189+
EXPECT_EQ(range01It, range01.end());
190+
}
191+
192+
TEST(RBinIndexRange, Full)
193+
{
194+
const auto underflow = RBinIndex::Underflow();
195+
const RBinIndex invalid;
196+
const auto full = CreateBinIndexRange(underflow, invalid, /*nNormalBins*/ 10);
197+
EXPECT_EQ(full.GetBegin(), underflow);
198+
EXPECT_EQ(full.GetEnd(), invalid);
199+
EXPECT_EQ(std::distance(full.begin(), full.end()), 12);
200+
201+
const std::vector binIndexes(full.begin(), full.end());
202+
ASSERT_EQ(binIndexes.size(), 12);
203+
EXPECT_TRUE(binIndexes.front().IsUnderflow());
204+
EXPECT_TRUE(binIndexes.back().IsOverflow());
205+
}

hist/histv7/test/hist_test.hxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
#include <ROOT/RAxes.hxx>
55
#include <ROOT/RBinIndex.hxx>
6+
#include <ROOT/RBinIndexRange.hxx>
67
#include <ROOT/RRegularAxis.hxx>
78
#include <ROOT/RVariableBinAxis.hxx>
89

910
#include "gtest/gtest.h"
1011

1112
using ROOT::Experimental::RBinIndex;
13+
using ROOT::Experimental::RBinIndexRange;
1214
using ROOT::Experimental::RRegularAxis;
1315
using ROOT::Experimental::RVariableBinAxis;
1416
using ROOT::Experimental::Internal::RAxes;

0 commit comments

Comments
 (0)