ostream improvements: support more accumulators, unicode plots (#317)

This commit is contained in:
Hans Dembinski 2021-04-23 20:15:12 +02:00 committed by GitHub
parent 8a402c7672
commit d60f96ded6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 451 additions and 110 deletions

View File

@ -16,7 +16,7 @@ env:
jobs:
cov:
runs-on: ubuntu-latest
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- name: Fetch Boost superproject
@ -42,10 +42,10 @@ jobs:
./b2 headers
# simulate bundled boost by moving the headers instead of symlinking
rm -rf boost/histogram.pp boost/histogram
rm -rf boost/histogram*
mv -f libs/histogram/include/boost/* boost
- name: Test gcc-8 cxxstd=latest coverage=on
- name: Test cxxstd=latest coverage=on
run: |
cd libs/histogram

View File

@ -63,8 +63,8 @@ jobs:
cd libs/histogram
../../b2 $B2_OPTS cxxstd=17 test//all
gcc7:
runs-on: ubuntu-latest
gcc5:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- name: Fetch Boost superproject
@ -80,10 +80,10 @@ jobs:
run: |
./bootstrap.sh
./b2 headers
- name: Test gcc-7 cxxstd=14
- name: Test cxxstd=14
run: |
cd libs/histogram
../../b2 $B2_OPTS toolset=gcc-7 cxxstd=14 test//all examples
../../b2 $B2_OPTS toolset=gcc-5 cxxstd=14 test//all examples
gcc10:
runs-on: ubuntu-latest
@ -102,7 +102,7 @@ jobs:
run: |
./bootstrap.sh
./b2 headers
- name: Test gcc-10 cxxstd=20 -O3 -funsafe-math-optimizations
- name: Test cxxstd=20 -O3 -funsafe-math-optimizations
run: |
cd libs/histogram
../../b2 $B2_OPTS toolset=gcc-10 cxxstd=20 cxxflags="-O3 -funsafe-math-optimizations" test//all examples
@ -124,7 +124,7 @@ jobs:
run: |
./bootstrap.sh
./b2 headers
- name: Test clang-6 cxxstd=17 ubsan asan
- name: Test cxxstd=17 ubsan asan
run: |
cd libs/histogram
../../b2 $B2_OPTS toolset=clang-6 cxxstd=17 variant=histogram_ubasan test//all

View File

@ -18,6 +18,10 @@ int main() {
std::ostringstream os;
// width of histogram can be set like this; if it is not set, the library attempts to
// determine the terminal width and choses the histogram width accordingly
os.width(78);
auto h1 = make_histogram(axis::regular<>(5, -1.0, 1.0, "axis 1"));
h1.at(0) = 2;
h1.at(1) = 4;
@ -38,15 +42,15 @@ int main() {
assert(
os.str() ==
"histogram(regular(5, -1, 1, metadata=\"axis 1\", options=underflow | overflow))\n"
" +-------------------------------------------------------------+\n"
"[-inf, -1) 0 | |\n"
"[ -1, -0.6) 2 |============================== |\n"
"[-0.6, -0.2) 4 |============================================================ |\n"
"[-0.2, 0.2) 3 |============================================= |\n"
"[ 0.2, 0.6) 0 | |\n"
"[ 0.6, 1) 1 |=============== |\n"
"[ 1, inf) 0 | |\n"
" +-------------------------------------------------------------+\n"
" ┌─────────────────────────────────────────────────────────────┐\n"
"[-inf, -1) 0 │ │\n"
"[ -1, -0.6) 2 │██████████████████████████████ │\n"
"[-0.6, -0.2) 4 │████████████████████████████████████████████████████████████ │\n"
"[-0.2, 0.2) 3 │█████████████████████████████████████████████ │\n"
"[ 0.2, 0.6) 0 │ │\n"
"[ 0.6, 1) 1 │███████████████ │\n"
"[ 1, inf) 0 │ │\n"
" └─────────────────────────────────────────────────────────────┘\n"
"histogram(\n"
" regular(2, -1, 1, metadata=\"axis 1\", options=underflow | overflow)\n"
" category(\"red\", \"blue\", metadata=\"axis 2\", options=overflow)\n"

View File

@ -0,0 +1,83 @@
// Copyright 2021 Hans Dembinski
//
// 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_DETAIL_TERM_INFO_HPP
#define BOOST_HISTOGRAM_DETAIL_TERM_INFO_HPP
#if defined __has_include
#if __has_include(<sys/ioctl.h>) && __has_include(<unistd.h>)
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#endif
#include <boost/config/workaround.hpp>
#include <cstdlib>
#include <cstring>
namespace boost {
namespace histogram {
namespace detail {
namespace term_info {
class env_t {
public:
env_t(const char* key) {
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0) // msvc complains about using std::getenv
_dupenv_s(&data, &size, key);
#else
data = std::getenv(key);
if (data) size = std::strlen(data);
#endif
}
~env_t() {
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
std::free(data);
#endif
}
bool contains(const char* s) {
const std::size_t n = std::strlen(s);
if (size < n) return false;
return std::strstr(data, s);
}
operator bool() { return size > 0; }
explicit operator int() { return size ? std::atoi(data) : 0; }
private:
char* data;
std::size_t size = 0;
};
inline bool utf8() {
// return false only if LANG exists and does not contain the string UTF
env_t env("LANG");
bool b = true;
if (env) b = env.contains("UTF") || env.contains("utf");
return b;
}
inline int width() {
int w = 0;
#if defined TIOCGWINSZ
struct winsize ws;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
w = std::max(static_cast<int>(ws.ws_col), 0); // not sure if ws_col can be less than 0
#endif
env_t env("COLUMNS");
const int col = std::max(static_cast<int>(env), 0);
// if both t and w are set, COLUMNS may be used to restrict width
return w == 0 ? col : std::min(col, w);
}
} // namespace term_info
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@ -11,7 +11,9 @@
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/axis/ostream.hpp>
#include <boost/histogram/detail/counting_streambuf.hpp>
#include <boost/histogram/detail/detect.hpp>
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/detail/term_info.hpp>
#include <boost/histogram/indexed.hpp>
#include <cmath>
#include <iomanip>
@ -162,19 +164,14 @@ void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
os << i;
}
template <class CharT>
struct line_t {
CharT ch;
int size;
struct line {
const char* ch;
const int size;
line(const char* a, int b) : ch{a}, size{std::max(b, 0)} {}
};
template <class CharT>
auto line(CharT c, int n) {
return line_t<CharT>{c, n};
}
template <class C, class T>
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, line_t<C>&& l) {
template <class T>
std::basic_ostream<char, T>& operator<<(std::basic_ostream<char, T>& os, line&& l) {
for (int i = 0; i < l.size; ++i) os << l.ch;
return os;
}
@ -191,13 +188,50 @@ void ostream_head(OStream& os, const Axis& ax, int index, double val) {
ax);
}
template <class OStream>
void ostream_bar(OStream& os, int zero_offset, double z, int width, bool utf8) {
int k = static_cast<int>(std::lround(z * width));
if (utf8) {
os << "";
if (z > 0) {
const char* scale[8] = {" ", "", "", "", "", "", "", ""};
int j = static_cast<int>(std::lround(8 * (z * width - k)));
if (j < 0) {
--k;
j += 8;
}
os << line(" ", zero_offset) << line("", k);
os << scale[j];
os << line(" ", width - zero_offset - k);
} else if (z < 0) {
os << line(" ", zero_offset + k) << line("", -k)
<< line(" ", width - zero_offset + 1);
} else {
os << line(" ", width + 1);
}
os << "\n";
} else {
os << " |";
if (z >= 0) {
os << line(" ", zero_offset) << line("=", k) << line(" ", width - zero_offset - k);
} else {
os << line(" ", zero_offset + k) << line("=", -k) << line(" ", width - zero_offset);
}
os << " |\n";
}
}
// cannot display generalized histograms yet; line not reachable by coverage tests
template <class OStream, class Histogram>
void ascii_plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
void plot(OStream&, const Histogram&, int, std::false_type) {} // LCOV_EXCL_LINE
template <class OStream, class Histogram>
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
void plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
if (w_total == 0) {
w_total = term_info::width();
if (w_total == 0 || w_total > 78) w_total = 78;
}
bool utf8 = term_info::utf8();
const auto& ax = h.axis();
@ -207,9 +241,10 @@ void ascii_plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
tabular_ostream_wrapper<OStream, 7> 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<double>(*v));
vmax = std::max(vmax, static_cast<double>(*v));
auto w = static_cast<double>(*v);
ostream_head(tos.row(), ax, v.index(), w);
vmin = std::min(vmin, w);
vmax = std::max(vmax, w);
}
tos.complete();
if (vmax == 0) vmax = 1;
@ -217,29 +252,31 @@ void ascii_plot(OStream& os, const Histogram& h, int w_total, std::true_type) {
// 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;
const int w_head = std::accumulate(tos.begin(), tos.end(), 0);
const int 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";
os << '\n' << line(" ", w_head + 1);
if (utf8)
os << "" << line("", w_bar + 1) << "\n";
else
os << '+' << line("-", w_bar + 1) << "+\n";
const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
for (auto&& v : indexed(h, coverage::all)) {
ostream_head(tos.row(), ax, v.index(), *v);
auto w = static_cast<double>(*v);
ostream_head(tos.row(), ax, v.index(), w);
// rest uses os, not tos
os << " |";
const int k = static_cast<int>(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";
ostream_bar(os, zero_offset, w / (vmax - vmin), w_bar, utf8);
}
// draw lower line
os << line(' ', w_head + 1) << '+' << line('-', w_bar + 1) << "+\n";
os << line(" ", w_head + 1);
if (utf8)
os << "" << line("", w_bar + 1) << "\n";
else
os << '+' << line("-", w_bar + 1) << "+\n";
}
template <class OStream, class Histogram>
@ -300,11 +337,12 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
using value_type = typename histogram<A, S>::value_type;
using convertible = detail::is_explicitly_convertible<value_type, double>;
// must be non-const to avoid a msvc warning about possible use of if constexpr
bool show_ascii = std::is_convertible<value_type, double>::value && h.rank() == 1;
if (show_ascii) {
bool show_plot = convertible::value && h.rank() == 1;
if (show_plot) {
detail::ostream(os, h, false);
detail::ascii_plot(os, h, w, std::is_convertible<value_type, double>{});
detail::plot(os, h, w, convertible{});
} else {
detail::ostream(os, h);
}
@ -314,9 +352,9 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
return os;
}
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DOXYGEN_INVOKED
#endif

View File

@ -18,7 +18,7 @@ endif()
set(BOOST_TEST_LINK_LIBRARIES Boost::histogram)
# keep in sync with Jamfile, this should be automatized...
# keep in sync with Jamfile, this should be automated...
boost_test(TYPE compile-fail SOURCES axis_category_fail0.cpp)
boost_test(TYPE compile-fail SOURCES axis_category_fail1.cpp)
boost_test(TYPE compile-fail SOURCES axis_category_fail2.cpp)
@ -119,6 +119,7 @@ endif()
# LINK_LIBRARIES Boost::serialization)
# boost_test(TYPE run SOURCES accumulators_serialization_test.cpp
# LINK_LIBRARIES Boost::serialization)
# boost_test(TYPE run SOURCES histogram_ostream_ascii_test.cpp)
# Workaround for gcc-5
if (NOT(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6))

View File

@ -1,6 +1,6 @@
# Copyright 2016-2017 Klemens David Morgenstern
# Copyright 2018 Mateusz Loskot <mateusz@loskot.net>
# Copyright 2018-2019 Hans Dembinski
# Copyright 2018-2021 Hans Dembinski
#
# Use, modification and distribution is subject to the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@ -82,7 +82,8 @@ alias cxx14 :
[ run histogram_growing_test.cpp ]
[ run histogram_mixed_test.cpp ]
[ run histogram_operators_test.cpp ]
[ run histogram_ostream_test.cpp ]
[ run histogram_ostream_test.cpp : : : <testing.launcher>"env LANG=UTF" ]
[ run histogram_ostream_ascii_test.cpp : : : <testing.launcher>"env LANG=FOO COLUMNS=20" ]
[ run histogram_test.cpp ]
[ run indexed_test.cpp ]
[ run storage_adaptor_test.cpp ]

View File

@ -6,7 +6,8 @@
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
"""
This test makes sure that all boost.histogram headers are included in the ODR test carried out in odr_main_test.cpp. See that file for details on why this test needed.
This test makes sure that all boost.histogram headers are included in the ODR test
carried out in odr_main_test.cpp. See that file for details on why this test needed.
"""
import os
@ -34,8 +35,9 @@ for root, dirs, files in os.walk(include_path):
def get_headers(filename):
with open(filename) as f:
for hdr in re.findall('^#include [<"]([^>"]+)[>"]', f.read(), re.MULTILINE):
with open(filename, "rb") as f:
for hdr in re.findall(b'^#include [<"]([^>"]+)[>"]', f.read(), re.MULTILINE):
hdr = hdr.decode()
if not hdr.startswith("boost/histogram"):
continue
yield hdr.replace("/", os.path.sep) # adapt the paths for Windows

View File

@ -0,0 +1,81 @@
// Copyright 2021 Hans Dembinski
//
// 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)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/thread_safe.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/make_histogram.hpp>
#include <boost/histogram/ostream.hpp>
#include <limits>
#include <sstream>
#include <string>
#include "throw_exception.hpp"
#include "utility_histogram.hpp"
using namespace boost::histogram;
template <class Histogram>
auto str(const Histogram& h, const unsigned width = 0) {
std::ostringstream os;
// BEGIN and END make nicer error messages
os << "BEGIN\n" << std::setw(width) << h << "END";
return os.str();
}
template <class Tag>
void run_tests() {
using I = axis::integer<>;
{
auto h = make(Tag(), I(0, 3));
h.at(0) = 1;
h.at(1) = 3;
h.at(2) = 2;
const auto expected = "BEGIN\n"
"histogram(integer(0, 3, options=underflow | overflow))\n"
" +-------------+\n"
"-1 0 | |\n"
" 0 1 |==== |\n"
" 1 3 |============ |\n"
" 2 2 |======== |\n"
" 3 0 | |\n"
" +-------------+\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
{
auto h = make(Tag(), I(0, 3));
h.at(0) = -1;
h.at(1) = 3;
h.at(2) = 2;
const auto expected = "BEGIN\n"
"histogram(integer(0, 3, options=underflow | overflow))\n"
" +------------+\n"
"-1 0 | |\n"
" 0 -1 |=== |\n"
" 1 3 | ======== |\n"
" 2 2 | ====== |\n"
" 3 0 | |\n"
" +------------+\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
}
int main() {
run_tests<static_tag>();
run_tests<dynamic_tag>();
return boost::report_errors();
}

View File

@ -6,8 +6,12 @@
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/core/lightweight_test.hpp>
#include <boost/histogram/accumulators/count.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/ostream.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/thread_safe.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/option.hpp>
@ -49,13 +53,13 @@ void run_tests() {
const auto expected =
"BEGIN\n"
"histogram(regular(3, -0.5, 1, options=underflow | overflow))\n"
" +------------------------------------------------------------+\n"
"[-inf, -0.5) 0 | |\n"
"[-0.5, 0) 1 |====== |\n"
"[ 0, 0.5) 10 |=========================================================== |\n"
"[ 0.5, 1) 5 |============================== |\n"
"[ 1, inf) 0 | |\n"
" +------------------------------------------------------------+\n"
" ┌────────────────────────────────────────────────────────────┐\n"
"[-inf, -0.5) 0 │ │\n"
"[-0.5, 0) 1 │█████▉ │\n"
"[ 0, 0.5) 10 │███████████████████████████████████████████████████████████ │\n"
"[ 0.5, 1) 5 │█████████████████████████████▌ │\n"
"[ 1, inf) 0 │ │\n"
" └────────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
@ -66,15 +70,15 @@ void run_tests() {
auto h = make(Tag(), R2(3, -0.5, 1.0));
h.at(0) = 1;
h.at(1) = 10;
h.at(2) = 2;
h.at(2) = 5;
const auto expected = "BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" +-----------------------+\n"
"[-0.5, 0) 1 |== |\n"
"[ 0, 0.5) 10 |====================== |\n"
"[ 0.5, 1) 2 |==== |\n"
" +-----------------------+\n"
" ┌───────────────────────┐\n"
"[-0.5, 0) 1 │██▎ │\n"
"[ 0, 0.5) 10 │██████████████████████ │\n"
"[ 0.5, 1) 5 │███████████ │\n"
" └───────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
@ -85,6 +89,85 @@ void run_tests() {
"histogram(regular(3, -0.5, 1, options=none))END");
}
// regular with accumulators::count
{
auto h =
make_s(Tag(), dense_storage<accumulators::count<double>>(), R2(3, -0.5, 1.0));
h.at(0) = 1;
h.at(1) = 10;
h.at(2) = 5;
const auto expected = "BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" ┌───────────────────────┐\n"
"[-0.5, 0) 1 │██▎ │\n"
"[ 0, 0.5) 10 │██████████████████████ │\n"
"[ 0.5, 1) 5 │███████████ │\n"
" └───────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
}
// regular with accumulators::thread_safe
{
auto h = make_s(Tag(), dense_storage<accumulators::thread_safe<double>>(),
R2(3, -0.5, 1.0));
h.at(0) = 1;
h.at(1) = 10;
h.at(2) = 5;
const auto expected = "BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" ┌───────────────────────┐\n"
"[-0.5, 0) 1 │██▎ │\n"
"[ 0, 0.5) 10 │██████████████████████ │\n"
"[ 0.5, 1) 5 │███████████ │\n"
" └───────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
}
// regular with accumulators::sum
{
auto h = make_s(Tag(), dense_storage<accumulators::sum<double>>(), R2(3, -0.5, 1.0));
h.at(0) = 1;
h.at(1) = 10;
h.at(2) = 5;
const auto expected = "BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" ┌───────────────────────┐\n"
"[-0.5, 0) 1 │██▎ │\n"
"[ 0, 0.5) 10 │██████████████████████ │\n"
"[ 0.5, 1) 5 │███████████ │\n"
" └───────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
}
// regular with accumulators::weighted_sum
{
auto h = make_s(Tag(), dense_storage<accumulators::weighted_sum<double>>(),
R2(3, -0.5, 1.0));
h.at(0) = 1;
h.at(1) = 10;
h.at(2) = 5;
const auto expected = "BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" ┌───────────────────────┐\n"
"[-0.5, 0) 1 │██▎ │\n"
"[ 0, 0.5) 10 │██████████████████████ │\n"
"[ 0.5, 1) 5 │███████████ │\n"
" └───────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
}
// regular2
{
auto h = make(Tag(), R2(3, -0.5, 1.0));
@ -95,11 +178,11 @@ void run_tests() {
const auto expected =
"BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))\n"
" +-------------------------------------------------------------+\n"
"[-0.5, 0) 1 | ========= |\n"
"[ 0, 0.5) -5 |=========================================== |\n"
"[ 0.5, 1) 2 | ================= |\n"
" +-------------------------------------------------------------+\n"
" ┌─────────────────────────────────────────────────────────────┐\n"
"[-0.5, 0) 1 │ ████████▋ │\n"
"[ 0, 0.5) -5 │███████████████████████████████████████████ │\n"
"[ 0.5, 1) 2 │ █████████████████▏│\n"
" └─────────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
@ -113,41 +196,89 @@ void run_tests() {
"BEGIN\n"
"histogram(regular(transform::log{}, 6, 0.001, 1000, metadata=\"foo\", "
"options=underflow | overflow))\n"
" +-----------------------------------------------------------+\n"
"[ 0, 0.001) 0 | |\n"
"[0.001, 0.01) 0 | |\n"
"[ 0.01, 0.1) 0 | |\n"
"[ 0.1, 1) 0 | |\n"
"[ 1, 10) 0 | |\n"
"[ 10, 100) 0 | |\n"
"[ 100, 1000) 0 | |\n"
"[ 1000, inf) 0 | |\n"
" +-----------------------------------------------------------+\n"
" ┌───────────────────────────────────────────────────────────┐\n"
"[ 0, 0.001) 0 │ │\n"
"[0.001, 0.01) 0 │ │\n"
"[ 0.01, 0.1) 0 │ │\n"
"[ 0.1, 1) 0 │ │\n"
"[ 1, 10) 0 │ │\n"
"[ 10, 100) 0 │ │\n"
"[ 100, 1000) 0 │ │\n"
"[ 1000, inf) 0 │ │\n"
" └───────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// subsampling
{
auto h = make(Tag(), I(-8, 9));
for (int i = -8; i < 9; ++i) h.at(i + 8) = i;
const auto expected = "BEGIN\n"
"histogram(integer(-8, 9, options=underflow | overflow))\n"
" ┌───┐\n"
"-9 0 │ │\n"
"-8 -8 │█ │\n"
"-7 -7 │█ │\n"
"-6 -6 │█ │\n"
"-5 -5 │█ │\n"
"-4 -4 │█ │\n"
"-3 -3 │ │\n"
"-2 -2 │ │\n"
"-1 -1 │ │\n"
" 0 0 │ │\n"
" 1 1 │ ▏ │\n"
" 2 2 │ ▎ │\n"
" 3 3 │ ▍ │\n"
" 4 4 │ ▌ │\n"
" 5 5 │ ▋ │\n"
" 6 6 │ ▊ │\n"
" 7 7 │ ▉ │\n"
" 8 8 │ █ │\n"
" 9 0 │ │\n"
" └───┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h, 11).c_str(), expected);
}
// integer
{
auto h = make(Tag(), I(0, 1));
h.at(0) = -10;
h.at(1) = 5;
auto h = make(Tag(), I(-8, 9));
for (int i = -8; i < 9; ++i) h.at(i + 8) = i;
const auto expected =
"BEGIN\n"
"histogram(integer(0, 1, options=underflow | overflow))\n"
" +---------------------------------------------------------------------+\n"
"-1 0 | |\n"
" 0 -10 |============================================= |\n"
" 1 5 | ======================= |\n"
" +---------------------------------------------------------------------+\n"
"histogram(integer(-8, 9, options=underflow | overflow))\n"
" ┌──────────────────────────────────────────────────────────────────────┐\n"
"-9 0 │ │\n"
"-8 -8 │███████████████████████████████████ │\n"
"-7 -7 │ ██████████████████████████████ │\n"
"-6 -6 │ ██████████████████████████ │\n"
"-5 -5 │ ██████████████████████ │\n"
"-4 -4 │ █████████████████ │\n"
"-3 -3 │ █████████████ │\n"
"-2 -2 │ █████████ │\n"
"-1 -1 │ ████ │\n"
" 0 0 │ │\n"
" 1 1 │ ████▍ │\n"
" 2 2 │ ████████▋ │\n"
" 3 3 │ ████████████▉ │\n"
" 4 4 │ █████████████████▎ │\n"
" 5 5 │ █████████████████████▌ │\n"
" 6 6 │ █████████████████████████▉ │\n"
" 7 7 │ ██████████████████████████████▎ │\n"
" 8 8 │ ██████████████████████████████████▌│\n"
" 9 0 │ │\n"
" └──────────────────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// catorgy<string>
// category<string>
{
auto h = make(Tag(), C({"a", "bb", "ccc", "dddd"}));
h.at(0) = 1.23;
@ -159,13 +290,13 @@ void run_tests() {
const auto expected =
"BEGIN\n"
"histogram(category(\"a\", \"bb\", \"ccc\", \"dddd\", options=overflow))\n"
" +------------------------------------------------------------+\n"
" a 1.23 |=========================================================== |\n"
" bb 1 |================================================ |\n"
" ccc 0.001235 | |\n"
" dddd 1.235e-12 | |\n"
"other nan | |\n"
" +------------------------------------------------------------+\n"
" ┌────────────────────────────────────────────────────────────┐\n"
" a 1.23 │███████████████████████████████████████████████████████████ │\n"
" bb 1 │████████████████████████████████████████████████ │\n"
" ccc 0.001235 │ │\n"
" dddd 1.235e-12 │ │\n"
"other nan │ │\n"
" └────────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
@ -187,10 +318,10 @@ void run_tests() {
"histogram(" +
detail::type_name<minimal_axis>() +
")\n"
" +------------------------------------------------------------------------+\n"
"0 3 |===================================================== |\n"
"1 4 |======================================================================= |\n"
" +------------------------------------------------------------------------+\n"
" ┌────────────────────────────────────────────────────────────────────────┐\n"
"0 3 │█████████████████████████████████████████████████████▎ │\n"
"1 4 │███████████████████████████████████████████████████████████████████████ │\n"
" └────────────────────────────────────────────────────────────────────────┘\n"
"END";
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected.c_str());
@ -235,7 +366,7 @@ void run_tests() {
int main() {
run_tests<static_tag>();
run_tests<dynamic_tag>();
// run_tests<dynamic_tag>();
{
// cannot make empty static histogram

View File

@ -6,7 +6,7 @@
# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
if [ -z $GCOV ]; then
# gcov-9, gcov-7, gcov-6 do not work
# gcov-10, gcov-9, gcov-7, gcov-6 do not work
for i in 8 5; do
if test $(which gcov-$i); then
GCOV=gcov-$i