Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:43:48

0001 // Copyright 2015-2019 Hans Dembinski
0002 // Copyright 2019 Przemyslaw Bartosik
0003 //
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE_1_0.txt
0006 // or copy at http://www.boost.org/LICENSE_1_0.txt)
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   \file boost/histogram/ostream.hpp
0029 
0030   A simple streaming operator for the histogram type. The text representation is
0031   rudimentary and not guaranteed to be stable between versions of Boost.Histogram. This
0032   header is not included by any other header and must be explicitly included to use the
0033   streaming operator.
0034 
0035   To use your own, simply include your own implementation instead of this header.
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_); // only call this once
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   // a value from histogram cell
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   // round edges to zero if deviation from zero is small
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 // cannot display generalized histograms yet; line not reachable by coverage tests
0225 template <class OStream, class Histogram>
0226 void plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
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   // value range; can be integer or float, positive or negative
0239   double vmin = 0;
0240   double vmax = 0;
0241   tabular_ostream_wrapper<OStream, 7> tos(os);
0242   // first pass to get widths
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   // calculate width useable by bar (notice extra space at top)
0253   // <-- head --> |<--- bar ---> |
0254   // w_head + 2 + 2
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   // draw upper line
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     // rest uses os, not tos
0271     ostream_bar(os, zero_offset, w / (vmax - vmin), w_bar, utf8);
0272   }
0273 
0274   // draw lower line
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 } // namespace detail
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   // save fmt
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   // must be non-const to avoid a msvc warning about possible use of if constexpr
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   // restore fmt
0351   os.flags(flags);
0352   return os;
0353 }
0354 
0355 #endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
0356 
0357 } // namespace histogram
0358 } // namespace boost
0359 
0360 #endif