]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/ostream.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / histogram / ostream.hpp
CommitLineData
92f5a8d4
TL
1// Copyright 2015-2019 Hans Dembinski
2// Copyright 2019 Przemyslaw Bartosik
3//
4// Distributed under the Boost Software License, Version 1.0.
5// (See accompanying file LICENSE_1_0.txt
6// or copy at http://www.boost.org/LICENSE_1_0.txt)
7
8#ifndef BOOST_HISTOGRAM_OSTREAM_HPP
9#define BOOST_HISTOGRAM_OSTREAM_HPP
10
11#include <boost/histogram/accumulators/ostream.hpp>
12#include <boost/histogram/axis/ostream.hpp>
92f5a8d4 13#include <boost/histogram/detail/counting_streambuf.hpp>
20effc67 14#include <boost/histogram/detail/priority.hpp>
92f5a8d4
TL
15#include <boost/histogram/indexed.hpp>
16#include <cmath>
17#include <iomanip>
18#include <ios>
19#include <limits>
20#include <numeric>
21#include <ostream>
22#include <streambuf>
23#include <type_traits>
24
25/**
26 \file boost/histogram/ostream.hpp
27
28 A simple streaming operator for the histogram type. The text representation is
29 rudimentary and not guaranteed to be stable between versions of Boost.Histogram. This
30 header is not included by any other header and must be explicitly included to use the
31 streaming operator.
32
20effc67 33 To use your own, simply include your own implementation instead of this header.
92f5a8d4
TL
34 */
35
36namespace boost {
37namespace histogram {
38namespace detail {
39
40template <class OStream, unsigned N>
41class tabular_ostream_wrapper : public std::array<int, N> {
42 using base_t = std::array<int, N>;
43 using char_type = typename OStream::char_type;
44 using traits_type = typename OStream::traits_type;
45
46public:
47 template <class T>
48 tabular_ostream_wrapper& operator<<(const T& t) {
49 if (collect_) {
20effc67 50 if (static_cast<unsigned>(iter_ - base_t::begin()) == size_) {
92f5a8d4 51 ++size_;
20effc67
TL
52 assert(size_ <= N);
53 assert(iter_ != end());
92f5a8d4
TL
54 *iter_ = 0;
55 }
20effc67 56 count_ = 0;
92f5a8d4 57 os_ << t;
20effc67 58 *iter_ = std::max(*iter_, static_cast<int>(count_));
92f5a8d4 59 } else {
20effc67 60 assert(iter_ != end());
92f5a8d4
TL
61 os_ << std::setw(*iter_) << t;
62 }
63 ++iter_;
64 return *this;
65 }
66
67 tabular_ostream_wrapper& operator<<(decltype(std::setprecision(0)) t) {
68 os_ << t;
69 return *this;
70 }
71
72 tabular_ostream_wrapper& operator<<(decltype(std::fixed) t) {
73 os_ << t;
74 return *this;
75 }
76
77 tabular_ostream_wrapper& row() {
78 iter_ = base_t::begin();
79 return *this;
80 }
81
20effc67
TL
82 explicit tabular_ostream_wrapper(OStream& os)
83 : os_(os), cbuf_(count_), orig_(os_.rdbuf(&cbuf_)) {}
92f5a8d4
TL
84
85 auto end() { return base_t::begin() + size_; }
86 auto end() const { return base_t::begin() + size_; }
87 auto cend() const { return base_t::cbegin() + size_; }
88
89 void complete() {
20effc67 90 assert(collect_); // only call this once
92f5a8d4
TL
91 collect_ = false;
92 os_.rdbuf(orig_);
93 }
94
95private:
96 typename base_t::iterator iter_ = base_t::begin();
20effc67
TL
97 unsigned size_ = 0;
98 std::streamsize count_ = 0;
92f5a8d4
TL
99 bool collect_ = true;
100 OStream& os_;
101 counting_streambuf<char_type, traits_type> cbuf_;
102 std::basic_streambuf<char_type, traits_type>* orig_;
103};
104
105template <class OStream, class T>
20effc67
TL
106void ostream_value_impl(OStream& os, const T& t,
107 decltype(static_cast<double>(t), priority<1>{})) {
108 // a value from histogram cell
109 const auto d = static_cast<double>(t);
110 if (std::numeric_limits<int>::min() <= d && d <= std::numeric_limits<int>::max()) {
111 const auto i = static_cast<int>(d);
112 if (i == d) {
113 os << i;
114 return;
115 }
116 }
117 os << std::defaultfloat << std::setprecision(4) << d;
118}
119
120template <class OStream, class T>
121void ostream_value_impl(OStream& os, const T& t, priority<0>) {
122 os << t;
123}
124
125template <class OStream, class T>
126void ostream_value(OStream& os, const T& t) {
127 ostream_value_impl(os << std::left, t, priority<1>{});
128}
129
130template <class OStream, class Axis>
131auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::true_type,
132 priority<1>) -> decltype((void)ax.value(i)) {
133 auto a = ax.value(i), b = ax.value(i + 1);
134 os << std::right << std::defaultfloat << std::setprecision(4);
135 // round edges to zero if deviation from zero is small
136 const auto eps = 1e-8 * std::abs(b - a);
137 if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
138 if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
139 os << "[" << a << ", " << b << ")";
92f5a8d4
TL
140}
141
142template <class OStream, class Axis>
20effc67
TL
143auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::false_type,
144 priority<1>) -> decltype((void)ax.value(i)) {
92f5a8d4 145 os << std::right;
20effc67 146 os << ax.value(i);
92f5a8d4
TL
147}
148
149template <class OStream, class... Ts>
20effc67
TL
150void ostream_bin(OStream& os, const axis::category<Ts...>& ax, axis::index_type i,
151 std::false_type, priority<1>) {
92f5a8d4
TL
152 os << std::right;
153 if (i < ax.size())
154 os << ax.value(i);
155 else
156 os << "other";
157}
158
20effc67
TL
159template <class OStream, class Axis, class B>
160void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
161 os << std::right;
162 os << i;
163}
164
92f5a8d4
TL
165template <class CharT>
166struct line_t {
167 CharT ch;
168 int size;
169};
170
171template <class CharT>
172auto line(CharT c, int n) {
173 return line_t<CharT>{c, n};
174}
175
176template <class C, class T>
177std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, line_t<C>&& l) {
178 for (int i = 0; i < l.size; ++i) os << l.ch;
179 return os;
180}
181
20effc67
TL
182template <class OStream, class Axis>
183void ostream_head(OStream& os, const Axis& ax, int index, double val) {
92f5a8d4
TL
184 axis::visit(
185 [&](const auto& ax) {
20effc67
TL
186 using A = std::decay_t<decltype(ax)>;
187 ostream_bin(os, ax, index, axis::traits::is_continuous<A>{}, priority<1>{});
92f5a8d4
TL
188 os << ' ';
189 ostream_value(os, val);
190 },
191 ax);
192}
193
20effc67 194// cannot display generalized histograms yet; line not reachable by coverage tests
92f5a8d4 195template <class OStream, class Histogram>
20effc67
TL
196void ascii_plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
197
198template <class OStream, class Histogram>
199void ascii_plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
92f5a8d4
TL
200 if (w_total == 0) w_total = 78; // TODO detect actual width of terminal
201
202 const auto& ax = h.axis();
203
204 // value range; can be integer or float, positive or negative
205 double vmin = 0;
206 double vmax = 0;
207 tabular_ostream_wrapper<OStream, 7> tos(os);
208 // first pass to get widths
209 for (auto&& v : indexed(h, coverage::all)) {
20effc67 210 ostream_head(tos.row(), ax, v.index(), *v);
92f5a8d4
TL
211 vmin = std::min(vmin, static_cast<double>(*v));
212 vmax = std::max(vmax, static_cast<double>(*v));
213 }
214 tos.complete();
215 if (vmax == 0) vmax = 1;
216
217 // calculate width useable by bar (notice extra space at top)
218 // <-- head --> |<--- bar ---> |
219 // w_head + 2 + 2
20effc67
TL
220 const auto w_head = std::accumulate(tos.begin(), tos.end(), 0);
221 const auto w_bar = w_total - 4 - w_head;
92f5a8d4
TL
222 if (w_bar < 0) return;
223
224 // draw upper line
225 os << '\n' << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
226
227 const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
228 for (auto&& v : indexed(h, coverage::all)) {
20effc67 229 ostream_head(tos.row(), ax, v.index(), *v);
92f5a8d4
TL
230 // rest uses os, not tos
231 os << " |";
232 const int k = static_cast<int>(std::lround(*v / (vmax - vmin) * w_bar));
233 if (k < 0) {
234 os << line(' ', zero_offset + k) << line('=', -k) << line(' ', w_bar - zero_offset);
235 } else {
236 os << line(' ', zero_offset) << line('=', k) << line(' ', w_bar - zero_offset - k);
237 }
238 os << " |\n";
239 }
240
241 // draw lower line
242 os << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
243}
244
245template <class OStream, class Histogram>
246void ostream(OStream& os, const Histogram& h, const bool show_values = true) {
247 os << "histogram(";
248
249 unsigned iaxis = 0;
250 const auto rank = h.rank();
251 h.for_each_axis([&](const auto& ax) {
92f5a8d4 252 if ((show_values && rank > 0) || rank > 1) os << "\n ";
20effc67 253 ostream_any(os, ax);
92f5a8d4
TL
254 });
255
256 if (show_values && rank > 0) {
257 tabular_ostream_wrapper<OStream, (BOOST_HISTOGRAM_DETAIL_AXES_LIMIT + 1)> tos(os);
258 for (auto&& v : indexed(h, coverage::all)) {
259 tos.row();
260 for (auto i : v.indices()) tos << std::right << i;
261 ostream_value(tos, *v);
262 }
263 tos.complete();
264
265 const int w_item = std::accumulate(tos.begin(), tos.end(), 0) + 4 + h.rank();
266 const int nrow = std::max(1, 65 / w_item);
267 int irow = 0;
268 for (auto&& v : indexed(h, coverage::all)) {
269 os << (irow == 0 ? "\n (" : " (");
270 tos.row();
271 iaxis = 0;
272 for (auto i : v.indices()) {
273 tos << std::right << i;
274 os << (++iaxis == h.rank() ? "):" : " ");
275 }
276 os << ' ';
277 ostream_value(tos, *v);
278 ++irow;
279 if (nrow > 0 && irow == nrow) irow = 0;
280 }
281 os << '\n';
282 }
283 os << ')';
284}
285
286} // namespace detail
287
288#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
289
f67539c2 290template <class CharT, class Traits, class A, class S>
92f5a8d4
TL
291std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
292 const histogram<A, S>& h) {
293 // save fmt
294 const auto flags = os.flags();
295
296 os.flags(std::ios::dec | std::ios::left);
297
298 const auto w = static_cast<int>(os.width());
299 os.width(0);
300
301 using value_type = typename histogram<A, S>::value_type;
20effc67
TL
302
303 // must be non-const to avoid a msvc warning about possible use of if constexpr
304 bool show_ascii = std::is_convertible<value_type, double>::value && h.rank() == 1;
305 if (show_ascii) {
306 detail::ostream(os, h, false);
307 detail::ascii_plot(os, h, w, std::is_convertible<value_type, double>{});
308 } else {
309 detail::ostream(os, h);
310 }
92f5a8d4
TL
311
312 // restore fmt
313 os.flags(flags);
314 return os;
315}
316
317} // namespace histogram
318} // namespace boost
319
320#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
321
322#endif