File indexing completed on 2025-01-30 09:43:48
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_HISTOGRAM_OSTREAM_HPP
0009 #define BOOST_HISTOGRAM_OSTREAM_HPP
0010
0011 #include <boost/histogram/accumulators/ostream.hpp>
0012 #include <boost/histogram/axis/ostream.hpp>
0013 #include <boost/histogram/detail/counting_streambuf.hpp>
0014 #include <boost/histogram/detail/detect.hpp>
0015 #include <boost/histogram/detail/priority.hpp>
0016 #include <boost/histogram/detail/term_info.hpp>
0017 #include <boost/histogram/indexed.hpp>
0018 #include <cmath>
0019 #include <iomanip>
0020 #include <ios>
0021 #include <limits>
0022 #include <numeric>
0023 #include <ostream>
0024 #include <streambuf>
0025 #include <type_traits>
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 namespace boost {
0039 namespace histogram {
0040 namespace detail {
0041
0042 template <class OStream, unsigned N>
0043 class tabular_ostream_wrapper : public std::array<int, N> {
0044 using base_t = std::array<int, N>;
0045 using char_type = typename OStream::char_type;
0046 using traits_type = typename OStream::traits_type;
0047
0048 public:
0049 template <class T>
0050 tabular_ostream_wrapper& operator<<(const T& t) {
0051 if (collect_) {
0052 if (static_cast<unsigned>(iter_ - base_t::begin()) == size_) {
0053 ++size_;
0054 assert(size_ <= N);
0055 assert(iter_ != end());
0056 *iter_ = 0;
0057 }
0058 count_ = 0;
0059 os_ << t;
0060 *iter_ = (std::max)(*iter_, static_cast<int>(count_));
0061 } else {
0062 assert(iter_ != end());
0063 os_ << std::setw(*iter_) << t;
0064 }
0065 ++iter_;
0066 return *this;
0067 }
0068
0069 tabular_ostream_wrapper& operator<<(decltype(std::setprecision(0)) t) {
0070 os_ << t;
0071 return *this;
0072 }
0073
0074 tabular_ostream_wrapper& operator<<(decltype(std::fixed) t) {
0075 os_ << t;
0076 return *this;
0077 }
0078
0079 tabular_ostream_wrapper& row() {
0080 iter_ = base_t::begin();
0081 return *this;
0082 }
0083
0084 explicit tabular_ostream_wrapper(OStream& os)
0085 : os_(os), cbuf_(count_), orig_(os_.rdbuf(&cbuf_)) {}
0086
0087 auto end() { return base_t::begin() + size_; }
0088 auto end() const { return base_t::begin() + size_; }
0089 auto cend() const { return base_t::cbegin() + size_; }
0090
0091 void complete() {
0092 assert(collect_);
0093 collect_ = false;
0094 os_.rdbuf(orig_);
0095 }
0096
0097 private:
0098 typename base_t::iterator iter_ = base_t::begin();
0099 unsigned size_ = 0;
0100 std::streamsize count_ = 0;
0101 bool collect_ = true;
0102 OStream& os_;
0103 counting_streambuf<char_type, traits_type> cbuf_;
0104 std::basic_streambuf<char_type, traits_type>* orig_;
0105 };
0106
0107 template <class OStream, class T>
0108 void ostream_value_impl(OStream& os, const T& t,
0109 decltype(static_cast<double>(t), priority<1>{})) {
0110
0111 const auto d = static_cast<double>(t);
0112 if ((std::numeric_limits<int>::min)() <= d && d <= (std::numeric_limits<int>::max)()) {
0113 const auto i = static_cast<int>(d);
0114 if (i == d) {
0115 os << i;
0116 return;
0117 }
0118 }
0119 os << std::defaultfloat << std::setprecision(4) << d;
0120 }
0121
0122 template <class OStream, class T>
0123 void ostream_value_impl(OStream& os, const T& t, priority<0>) {
0124 os << t;
0125 }
0126
0127 template <class OStream, class T>
0128 void ostream_value(OStream& os, const T& t) {
0129 ostream_value_impl(os << std::left, t, priority<1>{});
0130 }
0131
0132 template <class OStream, class Axis>
0133 auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::true_type,
0134 priority<1>) -> decltype((void)ax.value(i)) {
0135 auto a = ax.value(i), b = ax.value(i + 1);
0136 os << std::right << std::defaultfloat << std::setprecision(4);
0137
0138 const auto eps = 1e-8 * std::abs(b - a);
0139 if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
0140 if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
0141 os << "[" << a << ", " << b << ")";
0142 }
0143
0144 template <class OStream, class Axis>
0145 auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::false_type,
0146 priority<1>) -> decltype((void)ax.value(i)) {
0147 os << std::right;
0148 os << ax.value(i);
0149 }
0150
0151 template <class OStream, class... Ts>
0152 void ostream_bin(OStream& os, const axis::category<Ts...>& ax, axis::index_type i,
0153 std::false_type, priority<1>) {
0154 os << std::right;
0155 if (i < ax.size())
0156 os << ax.value(i);
0157 else
0158 os << "other";
0159 }
0160
0161 template <class OStream, class Axis, class B>
0162 void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
0163 os << std::right;
0164 os << i;
0165 }
0166
0167 struct line {
0168 const char* ch;
0169 const int size;
0170 line(const char* a, int b) : ch{a}, size{(std::max)(b, 0)} {}
0171 };
0172
0173 template <class T>
0174 std::basic_ostream<char, T>& operator<<(std::basic_ostream<char, T>& os, line&& l) {
0175 for (int i = 0; i < l.size; ++i) os << l.ch;
0176 return os;
0177 }
0178
0179 template <class OStream, class Axis>
0180 void ostream_head(OStream& os, const Axis& ax, int index, double val) {
0181 axis::visit(
0182 [&](const auto& ax) {
0183 using A = std::decay_t<decltype(ax)>;
0184 ostream_bin(os, ax, index, axis::traits::is_continuous<A>{}, priority<1>{});
0185 os << ' ';
0186 ostream_value(os, val);
0187 },
0188 ax);
0189 }
0190
0191 template <class OStream>
0192 void ostream_bar(OStream& os, int zero_offset, double z, int width, bool utf8) {
0193 int k = static_cast<int>(std::lround(z * width));
0194 if (utf8) {
0195 os << " │";
0196 if (z > 0) {
0197 const char* scale[8] = {" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"};
0198 int j = static_cast<int>(std::lround(8 * (z * width - k)));
0199 if (j < 0) {
0200 --k;
0201 j += 8;
0202 }
0203 os << line(" ", zero_offset) << line("█", k);
0204 os << scale[j];
0205 os << line(" ", width - zero_offset - k);
0206 } else if (z < 0) {
0207 os << line(" ", zero_offset + k) << line("█", -k)
0208 << line(" ", width - zero_offset + 1);
0209 } else {
0210 os << line(" ", width + 1);
0211 }
0212 os << "│\n";
0213 } else {
0214 os << " |";
0215 if (z >= 0) {
0216 os << line(" ", zero_offset) << line("=", k) << line(" ", width - zero_offset - k);
0217 } else {
0218 os << line(" ", zero_offset + k) << line("=", -k) << line(" ", width - zero_offset);
0219 }
0220 os << " |\n";
0221 }
0222 }
0223
0224
0225 template <class OStream, class Histogram>
0226 void plot(OStream&, const Histogram&, int, std::false_type) {}
0227
0228 template <class OStream, class Histogram>
0229 void plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
0230 if (w_total == 0) {
0231 w_total = term_info::width();
0232 if (w_total == 0 || w_total > 78) w_total = 78;
0233 }
0234 bool utf8 = term_info::utf8();
0235
0236 const auto& ax = h.axis();
0237
0238
0239 double vmin = 0;
0240 double vmax = 0;
0241 tabular_ostream_wrapper<OStream, 7> tos(os);
0242
0243 for (auto&& v : indexed(h, coverage::all)) {
0244 auto w = static_cast<double>(*v);
0245 ostream_head(tos.row(), ax, v.index(), w);
0246 vmin = (std::min)(vmin, w);
0247 vmax = (std::max)(vmax, w);
0248 }
0249 tos.complete();
0250 if (vmax == 0) vmax = 1;
0251
0252
0253
0254
0255 const int w_head = std::accumulate(tos.begin(), tos.end(), 0);
0256 const int w_bar = w_total - 4 - w_head;
0257 if (w_bar < 0) return;
0258
0259
0260 os << '\n' << line(" ", w_head + 1);
0261 if (utf8)
0262 os << "┌" << line("─", w_bar + 1) << "┐\n";
0263 else
0264 os << '+' << line("-", w_bar + 1) << "+\n";
0265
0266 const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
0267 for (auto&& v : indexed(h, coverage::all)) {
0268 auto w = static_cast<double>(*v);
0269 ostream_head(tos.row(), ax, v.index(), w);
0270
0271 ostream_bar(os, zero_offset, w / (vmax - vmin), w_bar, utf8);
0272 }
0273
0274
0275 os << line(" ", w_head + 1);
0276 if (utf8)
0277 os << "└" << line("─", w_bar + 1) << "┘\n";
0278 else
0279 os << '+' << line("-", w_bar + 1) << "+\n";
0280 }
0281
0282 template <class OStream, class Histogram>
0283 void ostream(OStream& os, const Histogram& h, const bool show_values = true) {
0284 os << "histogram(";
0285
0286 unsigned iaxis = 0;
0287 const auto rank = h.rank();
0288 h.for_each_axis([&](const auto& ax) {
0289 if ((show_values && rank > 0) || rank > 1) os << "\n ";
0290 ostream_any(os, ax);
0291 });
0292
0293 if (show_values && rank > 0) {
0294 tabular_ostream_wrapper<OStream, (BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1)> tos(os);
0295 for (auto&& v : indexed(h, coverage::all)) {
0296 tos.row();
0297 for (auto i : v.indices()) tos << std::right << i;
0298 ostream_value(tos, *v);
0299 }
0300 tos.complete();
0301
0302 const int w_item = std::accumulate(tos.begin(), tos.end(), 0) + 4 + h.rank();
0303 const int nrow = (std::max)(1, 65 / w_item);
0304 int irow = 0;
0305 for (auto&& v : indexed(h, coverage::all)) {
0306 os << (irow == 0 ? "\n (" : " (");
0307 tos.row();
0308 iaxis = 0;
0309 for (auto i : v.indices()) {
0310 tos << std::right << i;
0311 os << (++iaxis == h.rank() ? "):" : " ");
0312 }
0313 os << ' ';
0314 ostream_value(tos, *v);
0315 ++irow;
0316 if (nrow > 0 && irow == nrow) irow = 0;
0317 }
0318 os << '\n';
0319 }
0320 os << ')';
0321 }
0322
0323 }
0324
0325 #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
0326
0327 template <class CharT, class Traits, class A, class S>
0328 std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
0329 const histogram<A, S>& h) {
0330
0331 const auto flags = os.flags();
0332
0333 os.flags(std::ios::dec | std::ios::left);
0334
0335 const auto w = static_cast<int>(os.width());
0336 os.width(0);
0337
0338 using value_type = typename histogram<A, S>::value_type;
0339
0340 using convertible = detail::is_explicitly_convertible<value_type, double>;
0341
0342 bool show_plot = convertible::value && h.rank() == 1;
0343 if (show_plot) {
0344 detail::ostream(os, h, false);
0345 detail::plot(os, h, w, convertible{});
0346 } else {
0347 detail::ostream(os, h);
0348 }
0349
0350
0351 os.flags(flags);
0352 return os;
0353 }
0354
0355 #endif
0356
0357 }
0358 }
0359
0360 #endif