mirror of
https://github.com/boostorg/histogram.git
synced 2026-01-19 04:12:12 +00:00
Generalize indexed; allow interation over user-provided rectangular range (#321)
This commit is contained in:
@@ -25,7 +25,7 @@ def is_detail(x):
|
||||
if x.text is not None:
|
||||
if "detail" in x.text:
|
||||
return True
|
||||
m = re.match("(?:typename)? *([A-Za-z0-9_\:]+)", x.text)
|
||||
m = re.match(r"(?:typename)? *([A-Za-z0-9_\:]+)", x.text)
|
||||
if m is not None:
|
||||
s = m.group(1)
|
||||
if s.startswith("detail") or s.endswith("_impl"):
|
||||
@@ -122,6 +122,17 @@ for item in select(is_deprecated, "typedef"):
|
||||
)
|
||||
parent.remove(item)
|
||||
|
||||
# replace any typedef with "unspecified"
|
||||
for item in select(is_detail, "typedef"):
|
||||
log("replacing typedef", item.get("name"), 'with "unspecified"')
|
||||
name = item.get("name")
|
||||
item.clear()
|
||||
item.tag = "typedef"
|
||||
item.set("name", name)
|
||||
type = ET.Element("type")
|
||||
type.append(unspecified)
|
||||
item.append(type)
|
||||
|
||||
# hide private member functions
|
||||
for item in select(
|
||||
lambda x: x.get("name") == "private member functions", "method-group"
|
||||
|
||||
20
include/boost/histogram/detail/debug.hpp
Normal file
20
include/boost/histogram/detail/debug.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2019 Hans Dembinski
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_DEBUG_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_DEBUG_HPP
|
||||
|
||||
#warning "debug.hpp included"
|
||||
|
||||
#include <boost/histogram/detail/type_name.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#define DEBUG(x) \
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " [" \
|
||||
<< boost::histogram::detail::type_name<decltype(x)>() << "] " #x "=" << x \
|
||||
<< std::endl;
|
||||
|
||||
#endif
|
||||
@@ -11,16 +11,27 @@
|
||||
#include <boost/config.hpp> // BOOST_ATTRIBUTE_NODISCARD
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/iterator_adaptor.hpp>
|
||||
#include <boost/histogram/detail/operators.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <utility> // std::get
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
using std::get;
|
||||
|
||||
template <std::size_t I, class T>
|
||||
auto get(T&& t) -> decltype(t[0]) {
|
||||
return t[I];
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/** Coverage mode of the indexed range generator.
|
||||
|
||||
Defines options for the iteration strategy.
|
||||
@@ -32,8 +43,7 @@ enum class coverage {
|
||||
|
||||
/** Input iterator range over histogram bins with multi-dimensional index.
|
||||
|
||||
The iterator returned by begin() can only be incremented. begin() may only be called
|
||||
once, calling it a second time returns the end() iterator. If several copies of the
|
||||
The iterator returned by begin() can only be incremented. If several copies of the
|
||||
input iterators exist, the other copies become invalid if one of them is incremented.
|
||||
*/
|
||||
template <class Histogram>
|
||||
@@ -44,10 +54,13 @@ private:
|
||||
detail::buffer_size<typename std::decay_t<histogram_type>::axes_type>::value;
|
||||
|
||||
public:
|
||||
/// implementation detail
|
||||
using value_iterator = std::conditional_t<std::is_const<histogram_type>::value,
|
||||
typename histogram_type::const_iterator,
|
||||
typename histogram_type::iterator>;
|
||||
/// implementation detail
|
||||
using value_reference = typename std::iterator_traits<value_iterator>::reference;
|
||||
/// implementation detail
|
||||
using value_type = typename std::iterator_traits<value_iterator>::value_type;
|
||||
|
||||
class iterator;
|
||||
@@ -304,31 +317,36 @@ public:
|
||||
};
|
||||
|
||||
indexed_range(histogram_type& hist, coverage cov)
|
||||
: indexed_range(hist, make_range(hist, cov)) {}
|
||||
|
||||
template <class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
indexed_range(histogram_type& hist, Iterable&& range)
|
||||
: begin_(hist.begin(), hist), end_(hist.end(), hist) {
|
||||
begin_.indices_.hist_->for_each_axis([ca = begin_.indices_.begin(), cov,
|
||||
auto r_begin = std::begin(range);
|
||||
assert(std::distance(r_begin, std::end(range)) == hist.rank());
|
||||
|
||||
begin_.indices_.hist_->for_each_axis([ca = begin_.indices_.begin(), r_begin,
|
||||
stride = std::size_t{1},
|
||||
this](const auto& a) mutable {
|
||||
using opt = axis::traits::get_options<std::decay_t<decltype(a)>>;
|
||||
constexpr axis::index_type under = opt::test(axis::option::underflow);
|
||||
constexpr axis::index_type over = opt::test(axis::option::overflow);
|
||||
const auto size = a.size();
|
||||
|
||||
// -1 if underflow and cover all, else 0
|
||||
ca->begin = cov == coverage::all ? -under : 0;
|
||||
// size + 1 if overflow and cover all, else size
|
||||
ca->end = cov == coverage::all ? size + over : size;
|
||||
using opt = axis::traits::get_options<std::decay_t<decltype(a)>>;
|
||||
constexpr axis::index_type start = opt::test(axis::option::underflow) ? -1 : 0;
|
||||
const auto stop = size + (opt::test(axis::option::overflow) ? 1 : 0);
|
||||
|
||||
ca->begin = std::max(start, detail::get<0>(*r_begin));
|
||||
ca->end = std::min(stop, detail::get<1>(*r_begin));
|
||||
assert(ca->begin <= ca->end);
|
||||
ca->idx = ca->begin;
|
||||
|
||||
// if axis has *flow and coverage::all OR axis has no *flow:
|
||||
// begin + under == 0, size + over - end == 0
|
||||
// if axis has *flow and coverage::inner:
|
||||
// begin + under == 1, size + over - end == 1
|
||||
ca->begin_skip = static_cast<std::size_t>(ca->begin + under) * stride;
|
||||
ca->end_skip = static_cast<std::size_t>(size + over - ca->end) * stride;
|
||||
ca->begin_skip = static_cast<std::size_t>(ca->begin - start) * stride;
|
||||
ca->end_skip = static_cast<std::size_t>(stop - ca->end) * stride;
|
||||
begin_.iter_ += ca->begin_skip;
|
||||
|
||||
stride *= size + under + over;
|
||||
stride *= stop - start;
|
||||
|
||||
++ca;
|
||||
++r_begin;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -336,6 +354,22 @@ public:
|
||||
iterator end() noexcept { return end_; }
|
||||
|
||||
private:
|
||||
auto make_range(histogram_type& hist, coverage cov) {
|
||||
using range_item = std::array<axis::index_type, 2>;
|
||||
auto b = detail::make_stack_buffer<range_item>(unsafe_access::axes(hist));
|
||||
hist.for_each_axis([cov, it = std::begin(b)](const auto& a) mutable {
|
||||
(*it)[0] = 0;
|
||||
(*it)[1] = a.size();
|
||||
if (cov == coverage::all) {
|
||||
(*it)[0] -= 1;
|
||||
(*it)[1] += 1;
|
||||
} else
|
||||
assert(cov == coverage::inner);
|
||||
++it;
|
||||
});
|
||||
return b;
|
||||
}
|
||||
|
||||
iterator begin_, end_;
|
||||
};
|
||||
|
||||
@@ -366,6 +400,34 @@ auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
|
||||
cov};
|
||||
}
|
||||
|
||||
/** Generates and indexed range <a
|
||||
href="https://en.cppreference.com/w/cpp/named_req/ForwardIterator">forward iterators</a>
|
||||
over a rectangular region of histogram cells.
|
||||
|
||||
Use this in a range-based for loop. Example:
|
||||
```
|
||||
auto hist = make_histogram(axis::integer<>(0, 4), axis::integer<>(2, 6));
|
||||
axis::index_type range[2] = {{1, 3}, {0, 2}};
|
||||
for (auto&& x : indexed(hist, range)) { ... }
|
||||
```
|
||||
This skips the first and last index of the first axis, and the last two indices of the
|
||||
second.
|
||||
|
||||
@returns indexed_range
|
||||
|
||||
@param hist Reference to the histogram.
|
||||
@param range Iterable over items with two axis::index_type values, which mark the
|
||||
begin and end index of each axis. The length of the iterable must be
|
||||
equal to the rank of the histogram. The begin index must be smaller than
|
||||
the end index. Index ranges wider than the actual range are reduced to
|
||||
the actual range including underflow and overflow indices.
|
||||
*/
|
||||
template <class Histogram, class Iterable, class = detail::requires_iterable<Iterable>>
|
||||
auto indexed(Histogram&& hist, Iterable&& range) {
|
||||
return indexed_range<std::remove_reference_t<Histogram>>{std::forward<Histogram>(hist),
|
||||
std::forward<Iterable>(range)};
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ for dir in (pj(project_path, "test"), pj(project_path, "examples")):
|
||||
filename = os.path.join(dir, build_file)
|
||||
if not os.path.exists(filename):
|
||||
continue
|
||||
run = set(re.findall("([a-zA-Z0-9_]+\.cpp)", open(filename).read()))
|
||||
run = set(re.findall(r"([a-zA-Z0-9_]+\.cpp)", open(filename).read()))
|
||||
|
||||
diff = cpp - run
|
||||
diff.discard("check_cmake_version.cpp") # ignore
|
||||
|
||||
@@ -31,6 +31,8 @@ for root, dirs, files in os.walk(include_path):
|
||||
for fn in files:
|
||||
fn = os.path.join(root, fn)
|
||||
fn = fn[len(root_include_path) :]
|
||||
if fn.endswith("debug.hpp"): # is never included
|
||||
continue
|
||||
all_headers.add(fn)
|
||||
|
||||
|
||||
|
||||
@@ -156,6 +156,40 @@ void run_stdlib_tests(mp_list<Tag, Coverage>) {
|
||||
}
|
||||
}
|
||||
|
||||
template <class Tag>
|
||||
void run_indexed_with_range_tests(Tag) {
|
||||
{
|
||||
auto ax = axis::integer<>(0, 4);
|
||||
auto ay = axis::integer<>(0, 3);
|
||||
|
||||
auto h = make(Tag(), ax, ay);
|
||||
for (auto&& x : indexed(h, coverage::all)) x = x.index(0) + x.index(1);
|
||||
BOOST_TEST_EQ(h.at(0, 0), 0);
|
||||
BOOST_TEST_EQ(h.at(0, 1), 1);
|
||||
BOOST_TEST_EQ(h.at(0, 2), 2);
|
||||
BOOST_TEST_EQ(h.at(1, 0), 1);
|
||||
BOOST_TEST_EQ(h.at(1, 1), 2);
|
||||
BOOST_TEST_EQ(h.at(1, 2), 3);
|
||||
BOOST_TEST_EQ(h.at(2, 0), 2);
|
||||
BOOST_TEST_EQ(h.at(2, 1), 3);
|
||||
BOOST_TEST_EQ(h.at(2, 2), 4);
|
||||
BOOST_TEST_EQ(h.at(3, 0), 3);
|
||||
BOOST_TEST_EQ(h.at(3, 1), 4);
|
||||
BOOST_TEST_EQ(h.at(3, 2), 5);
|
||||
|
||||
/* x->
|
||||
y 0 1 2 3
|
||||
| 1 2 3 4
|
||||
v 2 3 4 5
|
||||
*/
|
||||
|
||||
int range[2][2] = {{1, 3}, {0, 2}};
|
||||
auto ir = indexed(h, range);
|
||||
auto sum = std::accumulate(ir.begin(), ir.end(), 0.0);
|
||||
BOOST_TEST_EQ(sum, 1 + 2 + 2 + 3);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
mp_for_each<mp_product<mp_list, mp_list<static_tag, dynamic_tag>,
|
||||
mp_list<std::integral_constant<coverage, coverage::inner>,
|
||||
@@ -166,5 +200,8 @@ int main() {
|
||||
run_density_tests(x);
|
||||
run_stdlib_tests(x);
|
||||
});
|
||||
|
||||
run_indexed_with_range_tests(static_tag{});
|
||||
run_indexed_with_range_tests(dynamic_tag{});
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user