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