mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-09 23:04:07 +00:00
Merged upgraded display.hpp code into histogram/ostream.hpp
supports all axis bin types and gracefully handles histograms with accumulators
This commit is contained in:
parent
98926dbbb5
commit
30899cb45d
@ -50,5 +50,5 @@ test_script:
|
||||
../../b2 $B2_OPTS toolset=clang cxxstd=14 variant=histogram_ubasan test//all
|
||||
|
||||
on_failure:
|
||||
## Uncomment the following line to stop VM and enable interactive login
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
# Uncomment the following line to stop VM and enable interactive login
|
||||
- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
@ -27,20 +27,10 @@ int main() {
|
||||
|
||||
assert(os.str() ==
|
||||
"histogram(\n"
|
||||
" regular(2, -1, 1, metadata=\"axis 1\", options=underflow | overflow),\n"
|
||||
" category(\"red\", \"blue\", metadata=\"axis 2\", options=overflow),\n"
|
||||
" 0: 0\n"
|
||||
" 1: 0\n"
|
||||
" 2: 0\n"
|
||||
" 3: 0\n"
|
||||
" 4: 0\n"
|
||||
" 5: 0\n"
|
||||
" 6: 0\n"
|
||||
" 7: 0\n"
|
||||
" 8: 0\n"
|
||||
" 9: 0\n"
|
||||
" 10: 0\n"
|
||||
" 11: 0\n"
|
||||
" regular(2, -1, 1, metadata=\"axis 1\", options=underflow | overflow)\n"
|
||||
" category(\"red\", \"blue\", metadata=\"axis 2\", options=overflow)\n"
|
||||
" (-1 0): 0 ( 0 0): 0 ( 1 0): 0 ( 2 0): 0 (-1 1): 0 ( 0 1): 0\n"
|
||||
" ( 1 1): 0 ( 2 1): 0 (-1 2): 0 ( 0 2): 0 ( 1 2): 0 ( 2 2): 0\n"
|
||||
")");
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef BOOST_HISTOGRAM_ACCUMULATORS_OSTREAM_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_OSTREAM_HPP
|
||||
|
||||
#include <boost/histogram/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
@ -26,34 +27,61 @@
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class CharT, class Traits, class T>
|
||||
std::basic_ostream<CharT, Traits>& handle_nonzero_width(
|
||||
std::basic_ostream<CharT, Traits>& os, const T& x) {
|
||||
const auto w = os.width();
|
||||
os.width(0);
|
||||
counting_streambuf<CharT, Traits> cb;
|
||||
const auto saved = os.rdbuf(&cb);
|
||||
os << x;
|
||||
os.rdbuf(saved);
|
||||
if (os.flags() & std::ios::left) {
|
||||
os << x;
|
||||
for (auto i = cb.count; i < w; ++i) os << os.fill();
|
||||
} else {
|
||||
for (auto i = cb.count; i < w; ++i) os << os.fill();
|
||||
os << x;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace accumulators {
|
||||
template <class CharT, class Traits, class W>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const sum<W>& x) {
|
||||
os << "sum(" << x.large() << " + " << x.small() << ")";
|
||||
return os;
|
||||
if (os.width() == 0) return os << "sum(" << x.large() << " + " << x.small() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class W>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const weighted_sum<W>& x) {
|
||||
os << "weighted_sum(" << x.value() << ", " << x.variance() << ")";
|
||||
return os;
|
||||
if (os.width() == 0)
|
||||
return os << "weighted_sum(" << x.value() << ", " << x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class W>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const mean<W>& x) {
|
||||
os << "mean(" << x.count() << ", " << x.value() << ", " << x.variance() << ")";
|
||||
return os;
|
||||
if (os.width() == 0)
|
||||
return os << "mean(" << x.count() << ", " << x.value() << ", " << x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class W>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const weighted_mean<W>& x) {
|
||||
os << "weighted_mean(" << x.sum_of_weights() << ", " << x.value() << ", "
|
||||
<< x.variance() << ")";
|
||||
return os;
|
||||
if (os.width() == 0)
|
||||
return os << "weighted_mean(" << x.sum_of_weights() << ", " << x.value() << ", "
|
||||
<< x.variance() << ")";
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class T>
|
||||
|
@ -182,11 +182,7 @@ std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
|
||||
using A = std::decay_t<decltype(x)>;
|
||||
detail::static_if<detail::is_streamable<A>>(
|
||||
[&os](const auto& x) { os << x; },
|
||||
[](const auto&) {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
detail::cat(detail::type_name<A>(), " is not streamable")));
|
||||
},
|
||||
x);
|
||||
[&os](const auto&) { os << "<unstreamable>"; }, x);
|
||||
},
|
||||
v);
|
||||
return os;
|
||||
|
@ -284,6 +284,12 @@ decltype(auto) get(const variant<Us...>& v) {
|
||||
return *tp;
|
||||
}
|
||||
|
||||
// pass-through version of visit for generic programming
|
||||
template <class Visitor, class T>
|
||||
decltype(auto) visit(Visitor&& vis, T&& var) {
|
||||
return std::forward<Visitor>(vis)(std::forward<T>(var));
|
||||
}
|
||||
|
||||
// pass-through version of get for generic programming
|
||||
template <class T, class U>
|
||||
decltype(auto) get(U&& u) {
|
||||
|
40
include/boost/histogram/detail/counting_streambuf.hpp
Normal file
40
include/boost/histogram/detail/counting_streambuf.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
// 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_COUNTING_STREAMBUF_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_COUNTING_STREAMBUF_HPP
|
||||
|
||||
#include <streambuf>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// detect how many characters will be printed by formatted output
|
||||
template <class CharT, class Traits = std::char_traits<CharT>>
|
||||
struct counting_streambuf : std::basic_streambuf<CharT, Traits> {
|
||||
using base_t = std::basic_streambuf<CharT, Traits>;
|
||||
using typename base_t::char_type;
|
||||
using typename base_t::int_type;
|
||||
|
||||
std::streamsize count = 0;
|
||||
|
||||
std::streamsize xsputn(const char_type* /* s */, std::streamsize n) override {
|
||||
count += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
int_type overflow(int_type ch) override {
|
||||
++count;
|
||||
return ch;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -110,7 +110,7 @@ void fill_n_indices(Index* indices, const std::size_t start, const std::size_t s
|
||||
for_each_axis(axes, [eit = extents, sit = shifts](const auto& a) mutable {
|
||||
*sit++ = 0;
|
||||
*eit++ = axis::traits::extent(a);
|
||||
});
|
||||
}); // LCOV_EXCL_LINE: gcc-8 is missing this line for no reason
|
||||
|
||||
// offset must be zero for growing axes
|
||||
using IsGrowing = has_growing_axis<Axes>;
|
||||
@ -118,7 +118,7 @@ void fill_n_indices(Index* indices, const std::size_t start, const std::size_t s
|
||||
for_each_axis(axes, [&, stride = static_cast<std::size_t>(1),
|
||||
pshift = shifts](auto& axis) mutable {
|
||||
using Axis = std::decay_t<decltype(axis)>;
|
||||
static_if<is_variant<T>>( // LCOV_EXCL_LINE: buggy in gcc-8, ok in gcc-5
|
||||
static_if<is_variant<T>>( // LCOV_EXCL_LINE: gcc-8 is missing this line for no reason
|
||||
[&](const auto& v) {
|
||||
variant2::visit(index_visitor<Index, Axis, IsGrowing>{axis, stride, start, size,
|
||||
indices, pshift},
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright 2015-2019 Hans Dembinski
|
||||
// Copyright (c) 2019 Przemyslaw Bartosik
|
||||
// Copyright 2019 Przemyslaw Bartosik
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
@ -8,16 +8,21 @@
|
||||
#ifndef BOOST_HISTOGRAM_OSTREAM_HPP
|
||||
#define BOOST_HISTOGRAM_OSTREAM_HPP
|
||||
|
||||
#include <algorithm> // max_element
|
||||
#include <boost/histogram.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/axis/ostream.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <cmath> // floor, pow
|
||||
#include <iomanip> // setw
|
||||
#include <iosfwd>
|
||||
#include <iostream> // cout
|
||||
#include <limits> // infinity
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/static_if.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
\file boost/histogram/ostream.hpp
|
||||
@ -30,19 +35,269 @@
|
||||
To you use your own, simply include your own implementation instead of this header.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class OStream, unsigned N>
|
||||
class tabular_ostream_wrapper : public std::array<int, N> {
|
||||
using base_t = std::array<int, N>;
|
||||
using char_type = typename OStream::char_type;
|
||||
using traits_type = typename OStream::traits_type;
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
tabular_ostream_wrapper& operator<<(const T& t) {
|
||||
if (collect_) {
|
||||
if (static_cast<std::size_t>(iter_ - base_t::begin()) == size_) {
|
||||
++size_;
|
||||
BOOST_ASSERT(size_ <= N);
|
||||
BOOST_ASSERT(iter_ != end());
|
||||
*iter_ = 0;
|
||||
}
|
||||
cbuf_.count = 0;
|
||||
os_ << t;
|
||||
*iter_ = std::max(*iter_, static_cast<int>(cbuf_.count));
|
||||
} else {
|
||||
BOOST_ASSERT(iter_ != end());
|
||||
os_ << std::setw(*iter_) << t;
|
||||
}
|
||||
++iter_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& operator<<(decltype(std::setprecision(0)) t) {
|
||||
os_ << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& operator<<(decltype(std::fixed) t) {
|
||||
os_ << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tabular_ostream_wrapper& row() {
|
||||
iter_ = base_t::begin();
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit tabular_ostream_wrapper(OStream& os) : os_(os), orig_(os_.rdbuf(&cbuf_)) {}
|
||||
|
||||
auto end() { return base_t::begin() + size_; }
|
||||
auto end() const { return base_t::begin() + size_; }
|
||||
auto cend() const { return base_t::cbegin() + size_; }
|
||||
|
||||
void complete() {
|
||||
BOOST_ASSERT(collect_); // only call this once
|
||||
collect_ = false;
|
||||
os_.rdbuf(orig_);
|
||||
}
|
||||
|
||||
private:
|
||||
typename base_t::iterator iter_ = base_t::begin();
|
||||
std::size_t size_ = 0;
|
||||
bool collect_ = true;
|
||||
OStream& os_;
|
||||
counting_streambuf<char_type, traits_type> cbuf_;
|
||||
std::basic_streambuf<char_type, traits_type>* orig_;
|
||||
};
|
||||
|
||||
template <class OStream, class T>
|
||||
void ostream_value(OStream& os, const T& val) {
|
||||
// a value from bin or histogram cell
|
||||
os << std::left;
|
||||
static_if_c<(std::is_convertible<T, double>::value && !std::is_integral<T>::value)>(
|
||||
[](auto& os, const auto& val) {
|
||||
const auto d = static_cast<double>(val);
|
||||
if (std::isfinite(d)) {
|
||||
const auto i = static_cast<std::int64_t>(d);
|
||||
if (i == d) {
|
||||
os << i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
os << std::defaultfloat << std::setprecision(4) << d;
|
||||
},
|
||||
[](auto& os, const auto& val) { os << val; }, os, val);
|
||||
}
|
||||
|
||||
template <class OStream, class Axis>
|
||||
void ostream_bin(OStream& os, const Axis& ax, const int i) {
|
||||
os << std::right;
|
||||
static_if<has_method_value<Axis>>(
|
||||
[&](const auto& ax) {
|
||||
static_if<axis::traits::is_continuous<Axis>>(
|
||||
[&](const auto& ax) {
|
||||
os << std::defaultfloat << std::setprecision(4);
|
||||
auto a = ax.value(i);
|
||||
auto b = ax.value(i + 1);
|
||||
// round bin edge to zero if deviation from zero is absolut and relatively
|
||||
// small
|
||||
const auto eps = 1e-8 * std::abs(b - a);
|
||||
if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
|
||||
if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
|
||||
os << "[" << a << ", " << b << ")";
|
||||
},
|
||||
[&](const auto& ax) { os << ax.value(i); }, ax);
|
||||
},
|
||||
[&](const auto&) { os << i; }, ax);
|
||||
}
|
||||
|
||||
template <class OStream, class... Ts>
|
||||
void ostream_bin(OStream& os, const axis::category<Ts...>& ax, const int i) {
|
||||
os << std::right;
|
||||
if (i < ax.size())
|
||||
os << ax.value(i);
|
||||
else
|
||||
os << "other";
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
struct line_t {
|
||||
CharT ch;
|
||||
int size;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
auto line(CharT c, int n) {
|
||||
return line_t<CharT>{c, n};
|
||||
}
|
||||
|
||||
template <class C, class T>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, line_t<C>&& l) {
|
||||
for (int i = 0; i < l.size; ++i) os << l.ch;
|
||||
return os;
|
||||
}
|
||||
|
||||
template <class OStream, class Axis, class T>
|
||||
void stream_head(OStream& os, const Axis& ax, int index, const T& val) {
|
||||
axis::visit(
|
||||
[&](const auto& ax) {
|
||||
ostream_bin(os, ax, index);
|
||||
os << ' ';
|
||||
ostream_value(os, val);
|
||||
},
|
||||
ax);
|
||||
}
|
||||
|
||||
template <class OStream, class Histogram>
|
||||
void ascii_plot(OStream& os, const Histogram& h, int w_total) {
|
||||
if (w_total == 0) w_total = 78; // TODO detect actual width of terminal
|
||||
|
||||
const auto& ax = h.axis();
|
||||
|
||||
// value range; can be integer or float, positive or negative
|
||||
double vmin = 0;
|
||||
double vmax = 0;
|
||||
tabular_ostream_wrapper<OStream, 7> tos(os);
|
||||
// first pass to get widths
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
stream_head(tos.row(), ax, v.index(), *v);
|
||||
vmin = std::min(vmin, static_cast<double>(*v));
|
||||
vmax = std::max(vmax, static_cast<double>(*v));
|
||||
}
|
||||
tos.complete();
|
||||
if (vmax == 0) vmax = 1;
|
||||
|
||||
// calculate width useable by bar (notice extra space at top)
|
||||
// <-- head --> |<--- bar ---> |
|
||||
// w_head + 2 + 2
|
||||
const int w_head = std::accumulate(tos.begin(), tos.end(), 0);
|
||||
const int w_bar = w_total - 4 - w_head;
|
||||
if (w_bar < 0) return;
|
||||
|
||||
// draw upper line
|
||||
os << '\n' << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
|
||||
|
||||
const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
stream_head(tos.row(), ax, v.index(), *v);
|
||||
// rest uses os, not tos
|
||||
os << " |";
|
||||
const int k = static_cast<int>(std::lround(*v / (vmax - vmin) * w_bar));
|
||||
if (k < 0) {
|
||||
os << line(' ', zero_offset + k) << line('=', -k) << line(' ', w_bar - zero_offset);
|
||||
} else {
|
||||
os << line(' ', zero_offset) << line('=', k) << line(' ', w_bar - zero_offset - k);
|
||||
}
|
||||
os << " |\n";
|
||||
}
|
||||
|
||||
// draw lower line
|
||||
os << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
|
||||
}
|
||||
|
||||
template <class OStream, class Histogram>
|
||||
void ostream(OStream& os, const Histogram& h, const bool show_values = true) {
|
||||
os << "histogram(";
|
||||
|
||||
unsigned iaxis = 0;
|
||||
const auto rank = h.rank();
|
||||
h.for_each_axis([&](const auto& ax) {
|
||||
using A = std::decay_t<decltype(ax)>;
|
||||
if ((show_values && rank > 0) || rank > 1) os << "\n ";
|
||||
static_if<is_streamable<A>>([&](const auto& ax) { os << ax; },
|
||||
[&](const auto&) { os << "<unstreamable>"; }, ax);
|
||||
});
|
||||
|
||||
if (show_values && rank > 0) {
|
||||
tabular_ostream_wrapper<OStream, (BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1)> tos(os);
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
tos.row();
|
||||
for (auto i : v.indices()) tos << std::right << i;
|
||||
ostream_value(tos, *v);
|
||||
}
|
||||
tos.complete();
|
||||
|
||||
const int w_item = std::accumulate(tos.begin(), tos.end(), 0) + 4 + h.rank();
|
||||
const int nrow = std::max(1, 65 / w_item);
|
||||
int irow = 0;
|
||||
for (auto&& v : indexed(h, coverage::all)) {
|
||||
os << (irow == 0 ? "\n (" : " (");
|
||||
tos.row();
|
||||
iaxis = 0;
|
||||
for (auto i : v.indices()) {
|
||||
tos << std::right << i;
|
||||
os << (++iaxis == h.rank() ? "):" : " ");
|
||||
}
|
||||
os << ' ';
|
||||
ostream_value(tos, *v);
|
||||
++irow;
|
||||
if (nrow > 0 && irow == nrow) irow = 0;
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
os << ')';
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
|
||||
template <typename CharT, typename Traits, typename A, typename S>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const histogram<A, S>& h) {
|
||||
os << "histogram(";
|
||||
h.for_each_axis([&](const auto& a) { os << "\n " << a << ","; });
|
||||
std::size_t i = 0;
|
||||
for (auto&& x : h) os << "\n " << i++ << ": " << x;
|
||||
os << (h.rank() ? "\n)" : ")");
|
||||
// save fmt
|
||||
const auto flags = os.flags();
|
||||
|
||||
os.flags(std::ios::dec | std::ios::left);
|
||||
|
||||
const auto w = static_cast<int>(os.width());
|
||||
os.width(0);
|
||||
|
||||
using value_type = typename histogram<A, S>::value_type;
|
||||
detail::static_if<std::is_convertible<value_type, double>>(
|
||||
[&os, w](const auto& h) {
|
||||
if (h.rank() == 1) {
|
||||
detail::ostream(os, h, false);
|
||||
detail::ascii_plot(os, h, w);
|
||||
} else
|
||||
detail::ostream(os, h);
|
||||
},
|
||||
[&os](const auto& h) { detail::ostream(os, h); }, h);
|
||||
|
||||
// restore fmt
|
||||
os.flags(flags);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,8 @@ boost_test(TYPE run SOURCES histogram_mixed_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES histogram_operators_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES histogram_ostream_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES histogram_test.cpp
|
||||
LIBRARIES Boost::histogram Boost::core)
|
||||
boost_test(TYPE run SOURCES indexed_test.cpp
|
||||
|
@ -65,6 +65,7 @@ alias cxx14 :
|
||||
[ run histogram_growing_test.cpp ]
|
||||
[ run histogram_mixed_test.cpp ]
|
||||
[ run histogram_operators_test.cpp ]
|
||||
[ run histogram_ostream_test.cpp ]
|
||||
[ run histogram_test.cpp ]
|
||||
[ run indexed_test.cpp ]
|
||||
[ run storage_adaptor_test.cpp ]
|
||||
|
@ -19,8 +19,13 @@ using namespace boost::histogram;
|
||||
using namespace std::literals;
|
||||
|
||||
template <class T>
|
||||
auto str(const T& t) {
|
||||
auto str(const T& t, int w = 0, bool left = true) {
|
||||
std::ostringstream os;
|
||||
os.width(w);
|
||||
if (left)
|
||||
os << std::left;
|
||||
else
|
||||
os << std::right;
|
||||
os << t;
|
||||
return os.str();
|
||||
}
|
||||
@ -30,6 +35,8 @@ int main() {
|
||||
using w_t = accumulators::weighted_sum<double>;
|
||||
w_t w;
|
||||
BOOST_TEST_EQ(str(w), "weighted_sum(0, 0)"s);
|
||||
BOOST_TEST_EQ(str(w, 20, false), " weighted_sum(0, 0)"s);
|
||||
BOOST_TEST_EQ(str(w, 20, true), "weighted_sum(0, 0) "s);
|
||||
BOOST_TEST_EQ(w, w_t{});
|
||||
|
||||
BOOST_TEST_EQ(w, w_t(0));
|
||||
@ -83,6 +90,8 @@ int main() {
|
||||
BOOST_TEST_EQ(a.variance(), 30);
|
||||
|
||||
BOOST_TEST_EQ(str(a), "mean(4, 10, 30)"s);
|
||||
BOOST_TEST_EQ(str(a, 20, false), " mean(4, 10, 30)"s);
|
||||
BOOST_TEST_EQ(str(a, 20, true), "mean(4, 10, 30) "s);
|
||||
|
||||
m_t b;
|
||||
b(1e8 + 4);
|
||||
@ -126,6 +135,8 @@ int main() {
|
||||
BOOST_TEST_IS_CLOSE(a.variance(), 0.8, 1e-3);
|
||||
|
||||
BOOST_TEST_EQ(str(a), "weighted_mean(2, 2, 0.8)"s);
|
||||
BOOST_TEST_EQ(str(a, 25, false), " weighted_mean(2, 2, 0.8)"s);
|
||||
BOOST_TEST_EQ(str(a, 25, true), "weighted_mean(2, 2, 0.8) "s);
|
||||
|
||||
auto b = a;
|
||||
b += a; // same as feeding all samples twice
|
||||
@ -148,6 +159,9 @@ int main() {
|
||||
BOOST_TEST_EQ(sum.large(), 1);
|
||||
BOOST_TEST_EQ(sum.small(), 0);
|
||||
BOOST_TEST_EQ(str(sum), "sum(1 + 0)"s);
|
||||
BOOST_TEST_EQ(str(sum, 15, false), " sum(1 + 0)"s);
|
||||
BOOST_TEST_EQ(str(sum, 15, true), "sum(1 + 0) "s);
|
||||
|
||||
sum += 1e100;
|
||||
BOOST_TEST_EQ(str(sum), "sum(1e+100 + 1)"s);
|
||||
++sum;
|
||||
|
@ -124,9 +124,7 @@ int main() {
|
||||
auto test = [](auto&& a, const char* ref) {
|
||||
using T = std::decay_t<decltype(a)>;
|
||||
axis::variant<T> axis(std::move(a));
|
||||
std::ostringstream os;
|
||||
os << axis;
|
||||
BOOST_TEST_EQ(os.str(), std::string(ref));
|
||||
BOOST_TEST_CSTR_EQ(detail::cat(axis).c_str(), ref);
|
||||
};
|
||||
|
||||
test(axis::regular<>(2, -1, 1, "regular1"),
|
||||
@ -143,9 +141,7 @@ int main() {
|
||||
auto test = [](auto&& a, const char* ref) {
|
||||
using T = std::decay_t<decltype(a)>;
|
||||
axis::variant<T> axis(std::move(a));
|
||||
std::ostringstream os;
|
||||
os << axis.bin(0);
|
||||
BOOST_TEST_EQ(os.str(), std::string(ref));
|
||||
BOOST_TEST_CSTR_EQ(detail::cat(axis.bin(0)).c_str(), ref);
|
||||
};
|
||||
|
||||
test(axis::regular<>(2, 1, 2), "[1, 1.5)");
|
||||
@ -194,7 +190,7 @@ int main() {
|
||||
BOOST_TEST_EQ(axis.index(9), 1);
|
||||
BOOST_TEST_EQ(axis.size(), 2);
|
||||
BOOST_TEST_EQ(axis.metadata(), axis::null_type{});
|
||||
BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error);
|
||||
BOOST_TEST_CSTR_EQ(detail::cat(axis).c_str(), "<unstreamable>");
|
||||
BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
|
||||
|
||||
axis = axis::category<std::string>({"A", "B"}, "category");
|
||||
|
@ -9,11 +9,13 @@
|
||||
#include <boost/histogram/accumulators/weighted_sum.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/common_type.hpp>
|
||||
#include <boost/histogram/detail/counting_streambuf.hpp>
|
||||
#include <boost/histogram/detail/non_member_container_access.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/literals.hpp>
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include <boost/histogram/unlimited_storage.hpp>
|
||||
#include <ostream>
|
||||
#include "std_ostream.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
@ -74,5 +76,17 @@ int main() {
|
||||
BOOST_TEST_EQ(dtl::size(d), 5u);
|
||||
}
|
||||
|
||||
// counting_streambuf
|
||||
{
|
||||
dtl::counting_streambuf<char> cbuf;
|
||||
std::ostream os(&cbuf);
|
||||
os.put('x');
|
||||
BOOST_TEST_EQ(cbuf.count, 1);
|
||||
os << 12;
|
||||
BOOST_TEST_EQ(cbuf.count, 3);
|
||||
os << "123";
|
||||
BOOST_TEST_EQ(cbuf.count, 6);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -164,36 +164,11 @@ void run_tests() {
|
||||
BOOST_TEST_THROWS(a *= b, std::invalid_argument);
|
||||
BOOST_TEST_THROWS(a /= b, std::invalid_argument);
|
||||
}
|
||||
|
||||
// histogram_ostream
|
||||
{
|
||||
auto a = make(Tag(), axis::regular<>(3, -1, 1, "r"));
|
||||
std::ostringstream os;
|
||||
os << a;
|
||||
BOOST_TEST_EQ(
|
||||
os.str(),
|
||||
std::string("histogram(\n"
|
||||
" regular(3, -1, 1, metadata=\"r\", options=underflow | overflow),\n"
|
||||
" 0: 0\n"
|
||||
" 1: 0\n"
|
||||
" 2: 0\n"
|
||||
" 3: 0\n"
|
||||
" 4: 0\n"
|
||||
")"));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
run_tests<static_tag>();
|
||||
run_tests<dynamic_tag>();
|
||||
|
||||
{
|
||||
// cannot make empty static histogram
|
||||
auto h = histogram<std::vector<axis::regular<>>>();
|
||||
std::ostringstream os;
|
||||
os << h;
|
||||
BOOST_TEST_EQ(os.str(), std::string("histogram()"));
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
249
test/histogram_ostream_test.cpp
Normal file
249
test/histogram_ostream_test.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
// Copyright 2019 Przemyslaw Bartosik
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/mean.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/option.hpp>
|
||||
#include <boost/histogram/axis/regular.hpp>
|
||||
#include <boost/histogram/make_histogram.hpp>
|
||||
#include <boost/histogram/ostream.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "throw_exception.hpp"
|
||||
#include "utility_histogram.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
|
||||
template <class Histogram>
|
||||
auto str(const Histogram& h, const unsigned width = 0) {
|
||||
std::ostringstream os;
|
||||
// BEGIN and END make nicer error messages
|
||||
os << "BEGIN\n" << std::setw(width) << h << "END";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template <class Tag>
|
||||
void run_tests() {
|
||||
using R = axis::regular<>;
|
||||
using R2 =
|
||||
axis::regular<double, boost::use_default, axis::null_type, axis::option::none_t>;
|
||||
using R3 = axis::regular<double, axis::transform::log>;
|
||||
using C = axis::category<std::string>;
|
||||
using I = axis::integer<>;
|
||||
|
||||
// regular
|
||||
{
|
||||
auto h = make(Tag(), R(3, -0.5, 1.0));
|
||||
h.at(0) = 1;
|
||||
h.at(1) = 10;
|
||||
h.at(2) = 5;
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(regular(3, -0.5, 1, options=underflow | overflow))\n"
|
||||
" +------------------------------------------------------------+\n"
|
||||
"[-inf, -0.5) 0 | |\n"
|
||||
"[-0.5, 0) 1 |====== |\n"
|
||||
"[ 0, 0.5) 10 |=========================================================== |\n"
|
||||
"[ 0.5, 1) 5 |============================== |\n"
|
||||
"[ 1, inf) 0 | |\n"
|
||||
" +------------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// regular, narrow
|
||||
{
|
||||
auto h = make(Tag(), R2(3, -0.5, 1.0));
|
||||
h.at(0) = 1;
|
||||
h.at(1) = 10;
|
||||
h.at(2) = 2;
|
||||
|
||||
const auto expected = "BEGIN\n"
|
||||
"histogram(regular(3, -0.5, 1, options=none))\n"
|
||||
" +-----------------------+\n"
|
||||
"[-0.5, 0) 1 |== |\n"
|
||||
"[ 0, 0.5) 10 |====================== |\n"
|
||||
"[ 0.5, 1) 2 |==== |\n"
|
||||
" +-----------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h, 40).c_str());
|
||||
|
||||
// too narrow
|
||||
BOOST_TEST_CSTR_EQ("BEGIN\n"
|
||||
"histogram(regular(3, -0.5, 1, options=none))END",
|
||||
str(h, 10).c_str());
|
||||
}
|
||||
|
||||
// regular2
|
||||
{
|
||||
auto h = make(Tag(), R2(3, -0.5, 1.0));
|
||||
h.at(0) = 1;
|
||||
h.at(1) = -5;
|
||||
h.at(2) = 2;
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(regular(3, -0.5, 1, options=none))\n"
|
||||
" +-------------------------------------------------------------+\n"
|
||||
"[-0.5, 0) 1 | ========= |\n"
|
||||
"[ 0, 0.5) -5 |=========================================== |\n"
|
||||
"[ 0.5, 1) 2 | ================= |\n"
|
||||
" +-------------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// regular with log
|
||||
{
|
||||
auto h = make(Tag(), R3(6, 1e-3, 1e3, "foo"));
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(regular_log(6, 0.001, 1000, metadata=\"foo\", options=underflow | "
|
||||
"overflow))\n"
|
||||
" +-----------------------------------------------------------+\n"
|
||||
"[ 0, 0.001) 0 | |\n"
|
||||
"[0.001, 0.01) 0 | |\n"
|
||||
"[ 0.01, 0.1) 0 | |\n"
|
||||
"[ 0.1, 1) 0 | |\n"
|
||||
"[ 1, 10) 0 | |\n"
|
||||
"[ 10, 100) 0 | |\n"
|
||||
"[ 100, 1000) 0 | |\n"
|
||||
"[ 1000, inf) 0 | |\n"
|
||||
" +-----------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// integer
|
||||
{
|
||||
auto h = make(Tag(), I(0, 1));
|
||||
h.at(0) = -10;
|
||||
h.at(1) = 5;
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(integer(0, 1, options=underflow | overflow))\n"
|
||||
" +---------------------------------------------------------------------+\n"
|
||||
"-1 0 | |\n"
|
||||
" 0 -10 |============================================= |\n"
|
||||
" 1 5 | ======================= |\n"
|
||||
" +---------------------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// catorgy<string>
|
||||
{
|
||||
auto h = make(Tag(), C({"a", "bb", "ccc", "dddd"}));
|
||||
h.at(0) = 1.23;
|
||||
h.at(1) = 1;
|
||||
h.at(2) = 1.2345789e-3;
|
||||
h.at(3) = 1.2345789e-12;
|
||||
h.at(4) = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(category(\"a\", \"bb\", \"ccc\", \"dddd\", options=overflow))\n"
|
||||
" +------------------------------------------------------------+\n"
|
||||
" a 1.23 |=========================================================== |\n"
|
||||
" bb 1 |================================================ |\n"
|
||||
" ccc 0.001235 | |\n"
|
||||
" dddd 1.235e-12 | |\n"
|
||||
"other nan | |\n"
|
||||
" +------------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// histogram with axis that has no value method
|
||||
{
|
||||
struct minimal_axis {
|
||||
int index(int x) const { return x % 2; }
|
||||
int size() const { return 2; }
|
||||
};
|
||||
|
||||
auto h = make(Tag(), minimal_axis{});
|
||||
h.at(0) = 3;
|
||||
h.at(1) = 4;
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(<unstreamable>)\n"
|
||||
" +------------------------------------------------------------------------+\n"
|
||||
"0 3 |===================================================== |\n"
|
||||
"1 4 |======================================================================= |\n"
|
||||
" +------------------------------------------------------------------------+\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// fallback for 2D
|
||||
{
|
||||
auto h = make(Tag(), R(1, -1, 1), R(2, -4, 7));
|
||||
h.at(-1, 0) = 1000;
|
||||
h.at(-1, -1) = 123;
|
||||
h.at(1, 0) = 1.23456789;
|
||||
h.at(-1, 2) = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
const auto expected =
|
||||
"BEGIN\n"
|
||||
"histogram(\n"
|
||||
" regular(1, -1, 1, options=underflow | overflow)\n"
|
||||
" regular(2, -4, 7, options=underflow | overflow)\n"
|
||||
" (-1 -1): 123 ( 0 -1): 0 ( 1 -1): 0 (-1 0): 1000 \n"
|
||||
" ( 0 0): 0 ( 1 0): 1.235 (-1 1): 0 ( 0 1): 0 \n"
|
||||
" ( 1 1): 0 (-1 2): nan ( 0 2): 0 ( 1 2): 0 \n"
|
||||
")END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
// fallback for profile
|
||||
{
|
||||
auto h = make_s(Tag(), profile_storage(), R(1, -1, 1));
|
||||
h.at(0) = accumulators::mean<>(10, 100, 1000);
|
||||
|
||||
const auto expected = "BEGIN\n"
|
||||
"histogram(\n"
|
||||
" regular(1, -1, 1, options=underflow | overflow)\n"
|
||||
" (-1): mean(0, 0, -0) ( 0): mean(10, 100, 1000)\n"
|
||||
" ( 1): mean(0, 0, -0) \n"
|
||||
")END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
run_tests<static_tag>();
|
||||
run_tests<dynamic_tag>();
|
||||
|
||||
{
|
||||
// cannot make empty static histogram
|
||||
auto h = histogram<std::vector<axis::regular<>>>();
|
||||
|
||||
const auto expected = "BEGIN\n"
|
||||
"histogram()END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user