/usr/include/boost/histogram
Edit: /usr/include/boost/histogram/ostream.hpp (9473B)
// Copyright 2015-2019 Hans Dembinski
// Copyright 2019 Przemyslaw Bartosik
//
// 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_OSTREAM_HPP
#define BOOST_HISTOGRAM_OSTREAM_HPP
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
\file boost/histogram/ostream.hpp
A simple streaming operator for the histogram type. The text representation is
rudimentary and not guaranteed to be stable between versions of Boost.Histogram. This
header is not included by any other header and must be explicitly included to use the
streaming operator.
To use your own, simply include your own implementation instead of this header.
*/
namespace boost {
namespace histogram {
namespace detail {
template
class tabular_ostream_wrapper : public std::array {
using base_t = std::array;
using char_type = typename OStream::char_type;
using traits_type = typename OStream::traits_type;
public:
template
tabular_ostream_wrapper& operator<<(const T& t) {
if (collect_) {
if (static_cast(iter_ - base_t::begin()) == size_) {
++size_;
assert(size_ <= N);
assert(iter_ != end());
*iter_ = 0;
}
count_ = 0;
os_ << t;
*iter_ = std::max(*iter_, static_cast(count_));
} else {
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), cbuf_(count_), 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() {
assert(collect_); // only call this once
collect_ = false;
os_.rdbuf(orig_);
}
private:
typename base_t::iterator iter_ = base_t::begin();
unsigned size_ = 0;
std::streamsize count_ = 0;
bool collect_ = true;
OStream& os_;
counting_streambuf cbuf_;
std::basic_streambuf* orig_;
};
template
void ostream_value_impl(OStream& os, const T& t,
decltype(static_cast(t), priority<1>{})) {
// a value from histogram cell
const auto d = static_cast(t);
if (std::numeric_limits::min() <= d && d <= std::numeric_limits::max()) {
const auto i = static_cast(d);
if (i == d) {
os << i;
return;
}
}
os << std::defaultfloat << std::setprecision(4) << d;
}
template
void ostream_value_impl(OStream& os, const T& t, priority<0>) {
os << t;
}
template
void ostream_value(OStream& os, const T& t) {
ostream_value_impl(os << std::left, t, priority<1>{});
}
template
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::true_type,
priority<1>) -> decltype((void)ax.value(i)) {
auto a = ax.value(i), b = ax.value(i + 1);
os << std::right << std::defaultfloat << std::setprecision(4);
// round edges to zero if deviation from zero is 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 << ")";
}
template
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::false_type,
priority<1>) -> decltype((void)ax.value(i)) {
os << std::right;
os << ax.value(i);
}
template
void ostream_bin(OStream& os, const axis::category& ax, axis::index_type i,
std::false_type, priority<1>) {
os << std::right;
if (i < ax.size())
os << ax.value(i);
else
os << "other";
}
template
void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
os << std::right;
os << i;
}
template
struct line_t {
CharT ch;
int size;
};
template
auto line(CharT c, int n) {
return line_t{c, n};
}
template
std::basic_ostream& operator<<(std::basic_ostream& os, line_t&& l) {
for (int i = 0; i < l.size; ++i) os << l.ch;
return os;
}
template
void ostream_head(OStream& os, const Axis& ax, int index, double val) {
axis::visit(
[&](const auto& ax) {
using A = std::decay_t;
ostream_bin(os, ax, index, axis::traits::is_continuous{}, priority<1>{});
os << ' ';
ostream_value(os, val);
},
ax);
}
// cannot display generalized histograms yet; line not reachable by coverage tests
template
void ascii_plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
template
void ascii_plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
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 tos(os);
// first pass to get widths
for (auto&& v : indexed(h, coverage::all)) {
ostream_head(tos.row(), ax, v.index(), *v);
vmin = std::min(vmin, static_cast(*v));
vmax = std::max(vmax, static_cast(*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 auto w_head = std::accumulate(tos.begin(), tos.end(), 0);
const auto 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(std::lround((-vmin) / (vmax - vmin) * w_bar));
for (auto&& v : indexed(h, coverage::all)) {
ostream_head(tos.row(), ax, v.index(), *v);
// rest uses os, not tos
os << " |";
const int k = static_cast(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
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) {
if ((show_values && rank > 0) || rank > 1) os << "\n ";
ostream_any(os, ax);
});
if (show_values && rank > 0) {
tabular_ostream_wrapper 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
std::basic_ostream& operator<<(std::basic_ostream& os,
const histogram& h) {
// save fmt
const auto flags = os.flags();
os.flags(std::ios::dec | std::ios::left);
const auto w = static_cast(os.width());
os.width(0);
using value_type = typename histogram::value_type;
// must be non-const to avoid a msvc warning about possible use of if constexpr
bool show_ascii = std::is_convertible::value && h.rank() == 1;
if (show_ascii) {
detail::ostream(os, h, false);
detail::ascii_plot(os, h, w, std::is_convertible{});
} else {
detail::ostream(os, h);
}
// restore fmt
os.flags(flags);
return os;
}
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif