Skip to content

Commit 78d03e6

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

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-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: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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::size_t;
66+
using value_type = RBinIndex;
67+
using pointer = RBinIndex *;
68+
using reference = RBinIndex &;
69+
using iterator_category = std::forward_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+
100+
friend bool operator==(Iterator lhs, Iterator rhs)
101+
{
102+
return lhs.fIndex == rhs.fIndex && lhs.fNNormalBins == rhs.fNNormalBins;
103+
}
104+
friend bool operator!=(Iterator lhs, Iterator rhs) { return !(lhs == rhs); }
105+
};
106+
107+
Iterator begin() const { return Iterator(fBegin, fNNormalBins); }
108+
Iterator end() const { return Iterator(fEnd, fNNormalBins); }
109+
110+
static RBinIndexRange Full(std::size_t nNormalBins)
111+
{
112+
RBinIndexRange full;
113+
full.fBegin = RBinIndex::Underflow();
114+
full.fEnd = RBinIndex();
115+
full.fNNormalBins = nNormalBins;
116+
return full;
117+
}
118+
};
119+
120+
namespace Internal {
121+
122+
/// Internal function to create RBinIndexRange.
123+
///
124+
/// \param[in] begin the begin of the bin index range (inclusive)
125+
/// \param[in] end the end of the bin index range (exclusive)
126+
/// \param[in] nNormalBins the number of normal bins, after which iteration advances to RBinIndex::Overflow()
127+
RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::size_t nNormalBins)
128+
{
129+
RBinIndexRange range;
130+
range.fBegin = begin;
131+
range.fEnd = end;
132+
range.fNNormalBins = nNormalBins;
133+
return range;
134+
}
135+
136+
} // namespace Internal
137+
138+
} // namespace Experimental
139+
} // namespace ROOT
140+
141+
#endif

hist/histv7/test/hist_index.cxx

Lines changed: 55 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,55 @@ 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_EQ(*range01It, index0);
187+
range01It++;
188+
EXPECT_EQ(range01It, range01.end());
189+
}
190+
191+
TEST(RBinIndexRange, Full)
192+
{
193+
const auto underflow = RBinIndex::Underflow();
194+
const RBinIndex invalid;
195+
const auto full = CreateBinIndexRange(underflow, invalid, /*nNormalBins*/ 10);
196+
EXPECT_EQ(full.GetBegin(), underflow);
197+
EXPECT_EQ(full.GetEnd(), invalid);
198+
EXPECT_EQ(std::distance(full.begin(), full.end()), 12);
199+
200+
const std::vector binIndexes(full.begin(), full.end());
201+
ASSERT_EQ(binIndexes.size(), 12);
202+
EXPECT_TRUE(binIndexes.front().IsUnderflow());
203+
EXPECT_TRUE(binIndexes.back().IsOverflow());
204+
}

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)