add binary axis

This commit is contained in:
Hans Dembinski 2020-06-06 23:45:12 +02:00 committed by GitHub
parent 1be4c4b3f2
commit 5253358988
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 480 additions and 223 deletions

View File

@ -38,6 +38,7 @@ matrix:
- python tools/boostdep/depinst/depinst.py --git_args "--depth 5 --jobs 3" throw_exception
- python tools/boostdep/depinst/depinst.py --git_args "--depth 5 --jobs 3" smart_ptr
- python tools/boostdep/depinst/depinst.py --git_args "--depth 5 --jobs 3" move
- git submodule update --init --depth 5 libs/function libs/type_index
script:
mkdir build && cd build &&

View File

@ -31,6 +31,7 @@ function(add_benchmark NAME)
endfunction()
add_benchmark(axis_size)
add_benchmark(axis_index)
add_benchmark(histogram_filling)
add_benchmark(histogram_iteration)

View File

@ -57,6 +57,12 @@ static void category(benchmark::State& state) {
for (auto _ : state) benchmark::DoNotOptimize(a.index(gen()));
}
static void binary(benchmark::State& state) {
auto a = axis::binary<>();
generator<uniform_int> gen(1);
for (auto _ : state) benchmark::DoNotOptimize(a.index(static_cast<bool>(gen())));
}
BENCHMARK_TEMPLATE(regular, uniform);
BENCHMARK_TEMPLATE(regular, normal);
BENCHMARK_TEMPLATE(circular, uniform);
@ -68,3 +74,4 @@ BENCHMARK_TEMPLATE(integer, double, normal);
BENCHMARK_TEMPLATE(variable, uniform)->RangeMultiplier(10)->Range(10, 10000);
BENCHMARK_TEMPLATE(variable, normal)->RangeMultiplier(10)->Range(10, 10000);
BENCHMARK(category)->RangeMultiplier(10)->Range(10, 10000);
BENCHMARK(binary);

View File

@ -20,7 +20,9 @@ int main() {
using variable = axis::variable<>;
using integer = axis::integer<>;
using category = axis::category<>;
using variant = axis::variant<regular, circular, variable, integer, category>;
using binary = axis::binary<>;
using binary_no_metadata = axis::binary<axis::null_type>;
using variant = axis::variant<regular, circular, variable, integer, category, binary>;
SHOW_SIZE(regular);
SHOW_SIZE(regular_float);
@ -30,5 +32,7 @@ int main() {
SHOW_SIZE(variable);
SHOW_SIZE(integer);
SHOW_SIZE(category);
SHOW_SIZE(binary);
SHOW_SIZE(binary_no_metadata);
SHOW_SIZE(variant);
}

View File

@ -57,10 +57,14 @@ alias threading :
<threading>multi
;
alias libserial : /boost/serialization//boost_serialization : <link>static ;
alias libserial :
/boost/serialization//boost_serialization :
<link>static <warnings>off <rtti>on
;
alias serial :
[ run guide_histogram_serialization.cpp libserial ]
[ run guide_histogram_serialization.cpp libserial ] :
<warnings>off
;
alias all : cxx14 threading serial ;

View File

@ -35,15 +35,16 @@ std::basic_ostream<CharT, Traits>& handle_nonzero_width(
std::basic_ostream<CharT, Traits>& os, const T& x) {
const auto w = os.width();
os.width(0);
counting_streambuf<CharT, Traits> cb;
const auto saved = os.rdbuf(&cb);
os << x;
os.rdbuf(saved);
std::streamsize count = 0;
{
auto g = make_count_guard(os, count);
os << x;
}
if (os.flags() & std::ios::left) {
os << x;
for (auto i = cb.count; i < w; ++i) os << os.fill();
for (auto i = count; i < w; ++i) os << os.fill();
} else {
for (auto i = cb.count; i < w; ++i) os << os.fill();
for (auto i = count; i < w; ++i) os << os.fill();
os << x;
}
return os;

View File

@ -17,6 +17,7 @@
[1]: histogram/reference.html#header.boost.histogram.axis.ostream_hpp
*/
#include <boost/histogram/axis/binary.hpp>
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis/regular.hpp>

View File

@ -0,0 +1,74 @@
// Copyright Hans Dembinski 2020
//
// 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_AXIS_BINARY_HPP
#define BOOST_HISTOGRAM_AXIS_BINARY_HPP
#include <boost/core/nvp.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/axis/metadata_base.hpp>
#include <boost/histogram/detail/relaxed_equal.hpp>
#include <boost/histogram/detail/replace_type.hpp>
#include <boost/histogram/fwd.hpp>
#include <string>
namespace boost {
namespace histogram {
namespace axis {
template <class MetaData>
class binary : public iterator_mixin<binary<MetaData>>, public metadata_base_t<MetaData> {
using value_type = bool;
using metadata_base = metadata_base_t<MetaData>;
using metadata_type = typename metadata_base::metadata_type;
public:
explicit binary(metadata_type meta = {}) : metadata_base(std::move(meta)) {}
index_type index(value_type x) const noexcept { return static_cast<index_type>(x); }
value_type value(index_type i) const noexcept { return static_cast<value_type>(i); }
value_type bin(index_type i) const noexcept { return value(i); }
index_type size() const noexcept { return 2; }
static constexpr bool inclusive() noexcept { return true; }
template <class M>
bool operator==(const binary<M>& o) const noexcept {
return detail::relaxed_equal{}(this->metadata(), o.metadata());
}
template <class M>
bool operator!=(const binary<M>& o) const noexcept {
return !operator==(o);
}
template <class Archive>
void serialize(Archive& ar, unsigned /* version */) {
ar& make_nvp("meta", this->metadata());
}
private:
template <class M>
friend class binary;
};
#if __cpp_deduction_guides >= 201606
binary()->binary<null_type>;
template <class M>
binary(M)->binary<detail::replace_type<std::decay_t<M>, const char*, std::string>>;
#endif
} // namespace axis
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_AXIS_BINARY_HPP

View File

@ -96,25 +96,9 @@ public:
/// Return index for value argument.
index_type index(value_type x) const noexcept {
return detail::static_if<std::is_floating_point<value_type>>(
[this](const auto z) -> index_type {
// need to handle NaN, cannot simply cast to int and call int-implementation
if (options_type::test(option::circular)) {
if (std::isfinite(z))
return static_cast<index_type>(std::floor(z) -
std::floor(z / this->size()) * this->size());
} else if (z < this->size())
return z >= 0 ? static_cast<index_type>(z) : -1;
return this->size();
},
[this](const auto z) -> index_type {
if (options_type::test(option::circular))
return static_cast<index_type>(z - std::floor(float(z) / this->size()) *
this->size());
if (z < this->size()) return z >= 0 ? z : -1;
return this->size();
},
x - min_);
return index_impl(options_type::test(axis::option::circular),
std::is_floating_point<value_type>{},
static_cast<double>(x - min_));
}
/// Returns index and shift (if axis has grown) for the passed argument.
@ -193,6 +177,24 @@ public:
}
private:
// axis not circular
template <class B>
index_type index_impl(std::false_type, B, double z) const noexcept {
if (z < size()) return z >= 0 ? static_cast<index_type>(z) : -1;
return size();
}
// value_type is integer, axis circular
index_type index_impl(std::true_type, std::false_type, double z) const noexcept {
return static_cast<index_type>(z - std::floor(z / size()) * size());
}
// value_type is floating point, must handle +/-infinite or nan, axis circular
index_type index_impl(std::true_type, std::true_type, double z) const noexcept {
if (std::isfinite(z)) return index_impl(std::true_type{}, std::false_type{}, z);
return z < size() ? -1 : size();
}
index_type size_{0};
value_type min_{0};

View File

@ -11,8 +11,8 @@
#include <boost/assert.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/detail/detect.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/detail/counting_streambuf.hpp>
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/detail/type_name.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
@ -40,26 +40,49 @@ namespace boost {
namespace histogram {
namespace detail {
inline const char* axis_suffix(const axis::transform::id&) { return ""; }
inline const char* axis_suffix(const axis::transform::log&) { return "_log"; }
inline const char* axis_suffix(const axis::transform::sqrt&) { return "_sqrt"; }
inline const char* axis_suffix(const axis::transform::pow&) { return "_pow"; }
template <class OStream, class T>
void stream_metadata(OStream& os, const T& t) {
detail::static_if<detail::is_streamable<T>>(
[&os](const auto& t) {
std::ostringstream oss;
oss << t;
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
},
[&os](const auto&) { os << ", metadata=" << detail::type_name<T>(); }, t);
auto ostream_any_impl(OStream& os, const T& t, priority<1>) -> decltype(os << t) {
return os << t;
}
template <class OStream, class T>
OStream& ostream_any_impl(OStream& os, const T&, priority<0>) {
return os << type_name<T>();
}
template <class OStream, class T>
OStream& ostream_any(OStream& os, const T& t) {
return ostream_any_impl(os, t, priority<1>{});
}
template <class OStream, class... Ts>
OStream& ostream_any_quoted(OStream& os, const std::basic_string<Ts...>& s) {
return os << std::quoted(s);
}
template <class OStream, class T>
OStream& ostream_any_quoted(OStream& os, const T& t) {
return ostream_any(os, t);
}
template <class... Ts, class T>
std::basic_ostream<Ts...>& ostream_metadata(std::basic_ostream<Ts...>& os, const T& t,
const char* prefix = ", ") {
std::streamsize count = 0;
{
auto g = make_count_guard(os, count);
ostream_any(os, t);
}
if (!count) return os;
os << prefix << "metadata=";
return ostream_any_quoted(os, t);
}
template <class OStream>
void stream_options(OStream& os, const unsigned bits) {
os << ", options=";
void ostream_options(OStream& os, const unsigned bits) {
bool first = true;
os << ", options=";
#define BOOST_HISTOGRAM_AXIS_OPTION_OSTREAM(x) \
if (bits & axis::option::x) { \
@ -81,24 +104,6 @@ void stream_options(OStream& os, const unsigned bits) {
if (first) os << "none";
}
template <class OStream, class T>
void stream_transform(OStream&, const T&) {}
template <class OStream>
void stream_transform(OStream& os, const axis::transform::pow& t) {
os << ", power=" << t.power;
}
template <class OStream, class T>
void stream_value(OStream& os, const T& t) {
os << t;
}
template <class OStream, class... Ts>
void stream_value(OStream& os, const std::basic_string<Ts...>& t) {
os << std::quoted(t);
}
} // namespace detail
namespace axis {
@ -114,40 +119,58 @@ std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const null_
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const interval_view<U>& i) {
os << "[" << i.lower() << ", " << i.upper() << ")";
return os;
return os << "[" << i.lower() << ", " << i.upper() << ")";
}
template <class... Ts, class U>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const polymorphic_bin<U>& i) {
if (i.is_discrete())
os << static_cast<double>(i);
else
os << "[" << i.lower() << ", " << i.upper() << ")";
if (i.is_discrete()) return os << static_cast<double>(i);
return os << "[" << i.lower() << ", " << i.upper() << ")";
}
namespace transform {
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const id&) {
return os;
}
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const log&) {
return os << "transform::log{}";
}
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const sqrt&) {
return os << "transform::sqrt{}";
}
template <class... Ts>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const pow& p) {
return os << "transform::pow{" << p.power << "}";
}
} // namespace transform
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const regular<Us...>& a) {
os << "regular" << detail::axis_suffix(a.transform()) << "(" << a.size() << ", "
<< a.value(0) << ", " << a.value(a.size());
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
detail::stream_transform(os, a.transform());
os << ")";
return os;
os << "regular(";
const auto pos = os.tellp();
os << a.transform();
if (os.tellp() > pos) os << ", ";
os << a.size() << ", " << a.value(0) << ", " << a.value(a.size());
detail::ostream_metadata(os, a.metadata());
detail::ostream_options(os, a.options());
return os << ")";
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const integer<Us...>& a) {
os << "integer(" << a.value(0) << ", " << a.value(a.size());
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
detail::ostream_metadata(os, a.metadata());
detail::ostream_options(os, a.options());
return os << ")";
}
template <class... Ts, class... Us>
@ -155,10 +178,9 @@ std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variable<Us...>& a) {
os << "variable(" << a.value(0);
for (index_type i = 1, n = a.size(); i <= n; ++i) { os << ", " << a.value(i); }
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
detail::ostream_metadata(os, a.metadata());
detail::ostream_options(os, a.options());
return os << ")";
}
template <class... Ts, class... Us>
@ -166,26 +188,25 @@ std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const category<Us...>& a) {
os << "category(";
for (index_type i = 0, n = a.size(); i < n; ++i) {
detail::stream_value(os, a.value(i));
detail::ostream_any_quoted(os, a.value(i));
os << (i == (a.size() - 1) ? "" : ", ");
}
detail::stream_metadata(os, a.metadata());
detail::stream_options(os, a.options());
os << ")";
return os;
detail::ostream_metadata(os, a.metadata());
detail::ostream_options(os, a.options());
return os << ")";
}
template <class... Ts, class M>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os, const binary<M>& a) {
os << "binary(";
detail::ostream_metadata(os, a.metadata(), "");
return os << ")";
}
template <class... Ts, class... Us>
std::basic_ostream<Ts...>& operator<<(std::basic_ostream<Ts...>& os,
const variant<Us...>& v) {
visit(
[&os](const auto& x) {
using A = std::decay_t<decltype(x)>;
detail::static_if<detail::is_streamable<A>>(
[&os](const auto& x) { os << x; },
[&os](const auto&) { os << "<unstreamable>"; }, x);
},
v);
visit([&os](const auto& x) { detail::ostream_any(os, x); }, v);
return os;
}

View File

@ -7,6 +7,8 @@
#ifndef BOOST_HISTOGRAM_DETAIL_COUNTING_STREAMBUF_HPP
#define BOOST_HISTOGRAM_DETAIL_COUNTING_STREAMBUF_HPP
#include <boost/core/exchange.hpp>
#include <ostream>
#include <streambuf>
namespace boost {
@ -20,19 +22,54 @@ struct counting_streambuf : std::basic_streambuf<CharT, Traits> {
using typename base_t::char_type;
using typename base_t::int_type;
std::streamsize count = 0;
std::streamsize* p_count;
counting_streambuf(std::streamsize& c) : p_count(&c) {}
std::streamsize xsputn(const char_type* /* s */, std::streamsize n) override {
count += n;
*p_count += n;
return n;
}
int_type overflow(int_type ch) override {
++count;
++*p_count;
return ch;
}
};
template <class C, class T>
struct count_guard {
using bos = std::basic_ostream<C, T>;
using bsb = std::basic_streambuf<C, T>;
counting_streambuf<C, T> csb;
bos* p_os;
bsb* p_rdbuf;
count_guard(bos& os, std::streamsize& s) : csb(s), p_os(&os), p_rdbuf(os.rdbuf(&csb)) {}
count_guard(count_guard&& o)
: csb(o.csb), p_os(boost::exchange(o.p_os, nullptr)), p_rdbuf(o.p_rdbuf) {}
count_guard& operator=(count_guard&& o) {
if (this != &o) {
csb = std::move(o.csb);
p_os = boost::exchange(o.p_os, nullptr);
p_rdbuf = o.p_rdbuf;
}
return *this;
}
~count_guard() {
if (p_os) p_os->rdbuf(p_rdbuf);
}
};
template <class C, class T>
count_guard<C, T> make_count_guard(std::basic_ostream<C, T>& os, std::streamsize& s) {
return {os, s};
}
} // namespace detail
} // namespace histogram
} // namespace boost

View File

@ -325,9 +325,9 @@ auto fill(std::true_type, ArgTraits, const std::size_t offset, S& storage, A& ax
growing{}, offset, storage, axes,
pack_args<ArgTraits::start::value, ArgTraits::nargs::value>(
typename ArgTraits::wpos{}, typename ArgTraits::spos{}, args));
return (BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank")),
storage.end());
return BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank")),
storage.end();
}
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)

View File

@ -21,7 +21,7 @@ std::string type_name_impl(boost::type<T>) {
}
template <class T>
std::string type_name_impl(boost::type<const T>) {
std::string type_name_impl(boost::type<T const>) {
return type_name_impl(boost::type<T>{}) + " const";
}

View File

@ -71,6 +71,9 @@ template <class Value = int, class MetaData = use_default, class Options = use_d
class Allocator = std::allocator<Value>>
class category;
template <class MetaData = use_default>
class binary;
template <class... Ts>
class variant;
@ -163,11 +166,11 @@ namespace detail {
template <class T>
struct buffer_size_impl
: std::integral_constant<unsigned, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT> {};
: std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT> {};
template <class... Ts>
struct buffer_size_impl<std::tuple<Ts...>>
: std::integral_constant<unsigned, sizeof...(Ts)> {};
: std::integral_constant<std::size_t, sizeof...(Ts)> {};
template <class T>
using buffer_size = typename buffer_size_impl<T>::type;

View File

@ -163,7 +163,7 @@ public:
unsigned d = 0;
iter_.indices_.hist_->for_each_axis([&](const auto& a) {
const auto w = axis::traits::width_as<double>(a, this->index(d++));
x *= w ? w : 1;
x *= w != 0 ? w : 1;
});
return get() / x;
}

View File

@ -8,11 +8,11 @@
#ifndef BOOST_HISTOGRAM_OSTREAM_HPP
#define BOOST_HISTOGRAM_OSTREAM_HPP
#include <boost/config/workaround.hpp>
#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> // is_streamable
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/indexed.hpp>
#include <cmath>
#include <iomanip>
@ -48,15 +48,15 @@ public:
template <class T>
tabular_ostream_wrapper& operator<<(const T& t) {
if (collect_) {
if (static_cast<std::size_t>(iter_ - base_t::begin()) == size_) {
if (static_cast<unsigned>(iter_ - base_t::begin()) == size_) {
++size_;
BOOST_ASSERT(size_ <= N);
BOOST_ASSERT(iter_ != end());
*iter_ = 0;
}
cbuf_.count = 0;
count_ = 0;
os_ << t;
*iter_ = std::max(*iter_, static_cast<int>(cbuf_.count));
*iter_ = std::max(*iter_, static_cast<int>(count_));
} else {
BOOST_ASSERT(iter_ != end());
os_ << std::setw(*iter_) << t;
@ -80,7 +80,8 @@ public:
return *this;
}
explicit tabular_ostream_wrapper(OStream& os) : os_(os), orig_(os_.rdbuf(&cbuf_)) {}
explicit tabular_ostream_wrapper(OStream& os)
: os_(os), cbuf_(count_), orig_(os_.rdbuf(&cbuf_)) {}
auto end() { return base_t::begin() + size_; }
auto end() const { return base_t::begin() + size_; }
@ -94,7 +95,8 @@ public:
private:
typename base_t::iterator iter_ = base_t::begin();
std::size_t size_ = 0;
unsigned size_ = 0;
std::streamsize count_ = 0;
bool collect_ = true;
OStream& os_;
counting_streambuf<char_type, traits_type> cbuf_;
@ -102,48 +104,52 @@ private:
};
template <class OStream, class T>
void ostream_value(OStream& os, const T& val) {
// a value from bin or histogram cell
os << std::left;
static_if_c<(std::is_convertible<T, double>::value && !std::is_integral<T>::value)>(
[](auto& os, const auto& val) {
const auto d = static_cast<double>(val);
if (std::isfinite(d)) {
const auto i = static_cast<std::int64_t>(d);
if (i == d) {
os << i;
return;
}
}
os << std::defaultfloat << std::setprecision(4) << d;
},
[](auto& os, const auto& val) { os << val; }, os, val);
void ostream_value_impl(OStream& os, const T& t,
decltype(static_cast<double>(t), priority<1>{})) {
// a value from histogram cell
const auto d = static_cast<double>(t);
if (std::numeric_limits<int>::min() <= d && d <= std::numeric_limits<int>::max()) {
const auto i = static_cast<int>(d);
if (i == d) {
os << i;
return;
}
}
os << std::defaultfloat << std::setprecision(4) << d;
}
template <class OStream, class T>
void ostream_value_impl(OStream& os, const T& t, priority<0>) {
os << t;
}
template <class OStream, class T>
void ostream_value(OStream& os, const T& t) {
ostream_value_impl(os << std::left, t, priority<1>{});
}
template <class OStream, class Axis>
void ostream_bin(OStream& os, const Axis& ax, const int i) {
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::true_type,
priority<1>) -> decltype((void)ax.value(i)) {
auto a = ax.value(i), b = ax.value(i + 1);
os << std::right << std::defaultfloat << std::setprecision(4);
// round edges to zero if deviation from zero is small
const auto eps = 1e-8 * std::abs(b - a);
if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
os << "[" << a << ", " << b << ")";
}
template <class OStream, class Axis>
auto ostream_bin(OStream& os, const Axis& ax, axis::index_type i, std::false_type,
priority<1>) -> decltype((void)ax.value(i)) {
os << std::right;
static_if<has_method_value<Axis>>(
[&](const auto& ax) {
static_if<axis::traits::is_continuous<Axis>>(
[&](const auto& ax) {
os << std::defaultfloat << std::setprecision(4);
auto a = ax.value(i);
auto b = ax.value(i + 1);
// round bin edge to zero if deviation from zero is absolut and relatively
// small
const auto eps = 1e-8 * std::abs(b - a);
if (std::abs(a) < 1e-14 && std::abs(a) < eps) a = 0;
if (std::abs(b) < 1e-14 && std::abs(b) < eps) b = 0;
os << "[" << a << ", " << b << ")";
},
[&](const auto& ax) { os << ax.value(i); }, ax);
},
[&](const auto&) { os << i; }, ax);
os << ax.value(i);
}
template <class OStream, class... Ts>
void ostream_bin(OStream& os, const axis::category<Ts...>& ax, const int i) {
void ostream_bin(OStream& os, const axis::category<Ts...>& ax, axis::index_type i,
std::false_type, priority<1>) {
os << std::right;
if (i < ax.size())
os << ax.value(i);
@ -151,6 +157,12 @@ void ostream_bin(OStream& os, const axis::category<Ts...>& ax, const int i) {
os << "other";
}
template <class OStream, class Axis, class B>
void ostream_bin(OStream& os, const Axis&, axis::index_type i, B, priority<0>) {
os << std::right;
os << i;
}
template <class CharT>
struct line_t {
CharT ch;
@ -168,19 +180,24 @@ std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os, line_t<C>&& l
return os;
}
template <class OStream, class Axis, class T>
void stream_head(OStream& os, const Axis& ax, int index, const T& val) {
template <class OStream, class Axis>
void ostream_head(OStream& os, const Axis& ax, int index, double val) {
axis::visit(
[&](const auto& ax) {
ostream_bin(os, ax, index);
using A = std::decay_t<decltype(ax)>;
ostream_bin(os, ax, index, axis::traits::is_continuous<A>{}, priority<1>{});
os << ' ';
ostream_value(os, val);
},
ax);
}
// cannot display generalized histograms yet
template <class OStream, class Histogram>
void ascii_plot(OStream& os, const Histogram& h, int w_total) {
void ascii_plot(OStream&, const Histogram&, int, std::false_type) {}
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
const auto& ax = h.axis();
@ -191,7 +208,7 @@ void ascii_plot(OStream& os, const Histogram& h, int w_total) {
tabular_ostream_wrapper<OStream, 7> tos(os);
// first pass to get widths
for (auto&& v : indexed(h, coverage::all)) {
stream_head(tos.row(), ax, v.index(), *v);
ostream_head(tos.row(), ax, v.index(), *v);
vmin = std::min(vmin, static_cast<double>(*v));
vmax = std::max(vmax, static_cast<double>(*v));
}
@ -201,8 +218,8 @@ void ascii_plot(OStream& os, const Histogram& h, int w_total) {
// calculate width useable by bar (notice extra space at top)
// <-- head --> |<--- bar ---> |
// w_head + 2 + 2
const int w_head = std::accumulate(tos.begin(), tos.end(), 0);
const int w_bar = w_total - 4 - w_head;
const auto w_head = std::accumulate(tos.begin(), tos.end(), 0);
const auto w_bar = w_total - 4 - w_head;
if (w_bar < 0) return;
// draw upper line
@ -210,7 +227,7 @@ void ascii_plot(OStream& os, const Histogram& h, int w_total) {
const int zero_offset = static_cast<int>(std::lround((-vmin) / (vmax - vmin) * w_bar));
for (auto&& v : indexed(h, coverage::all)) {
stream_head(tos.row(), ax, v.index(), *v);
ostream_head(tos.row(), ax, v.index(), *v);
// rest uses os, not tos
os << " |";
const int k = static_cast<int>(std::lround(*v / (vmax - vmin) * w_bar));
@ -233,10 +250,8 @@ void ostream(OStream& os, const Histogram& h, const bool show_values = true) {
unsigned iaxis = 0;
const auto rank = h.rank();
h.for_each_axis([&](const auto& ax) {
using A = std::decay_t<decltype(ax)>;
if ((show_values && rank > 0) || rank > 1) os << "\n ";
static_if<is_streamable<A>>([&](const auto& ax) { os << ax; },
[&](const auto&) { os << "<unstreamable>"; }, ax);
ostream_any(os, ax);
});
if (show_values && rank > 0) {
@ -285,15 +300,19 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
os.width(0);
using value_type = typename histogram<A, S>::value_type;
detail::static_if<std::is_convertible<value_type, double>>(
[&os, w](const auto& h) {
if (h.rank() == 1) {
detail::ostream(os, h, false);
detail::ascii_plot(os, h, w);
} else
detail::ostream(os, h);
},
[&os](const auto& h) { detail::ostream(os, h); }, h);
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(disable : 4127) // warning to use `if constexpr`
#endif
if (std::is_convertible<value_type, double>::value && h.rank() == 1) {
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(default : 4127)
#endif
detail::ostream(os, h, false);
detail::ascii_plot(os, h, w, std::is_convertible<value_type, double>{});
} else {
detail::ostream(os, h);
}
// restore fmt
os.flags(flags);

View File

@ -8,13 +8,8 @@
#ifndef BOOST_HISTOGRAM_UNLIMTED_STORAGE_HPP
#define BOOST_HISTOGRAM_UNLIMTED_STORAGE_HPP
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(disable : 4996) // temporary fix (deprecation) until fixed in Boost Core
#endif
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/config/workaround.hpp>
#include <boost/config.hpp>
#include <boost/core/alloc_construct.hpp>
#include <boost/core/exchange.hpp>
@ -643,8 +638,4 @@ private:
} // namespace histogram
} // namespace boost
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(default: 4996)
#endif
#endif

View File

@ -51,11 +51,11 @@ boost_test(TYPE run SOURCES algorithm_project_test.cpp)
boost_test(TYPE run SOURCES algorithm_reduce_test.cpp)
boost_test(TYPE run SOURCES algorithm_sum_test.cpp)
boost_test(TYPE run SOURCES algorithm_empty_test.cpp)
boost_test(TYPE run SOURCES axis_binary_test.cpp)
boost_test(TYPE run SOURCES axis_category_test.cpp)
boost_test(TYPE run SOURCES axis_integer_test.cpp)
boost_test(TYPE run SOURCES axis_option_test.cpp)
boost_test(TYPE run SOURCES axis_regular_test.cpp)
boost_test(TYPE run SOURCES axis_size.cpp)
boost_test(TYPE run SOURCES axis_traits_test.cpp)
boost_test(TYPE run SOURCES axis_variable_test.cpp)
boost_test(TYPE run SOURCES axis_variant_test.cpp)

View File

@ -35,6 +35,7 @@ testing.make-test run-pyd : check_build_system.py :
testing.make-test run-pyd : check_odr_test.py : <dependency>odr_test.cpp ;
alias odr :
[ link odr_main_test.cpp odr_test.cpp ]
: <warnings>off
;
alias cxx14 :
@ -51,11 +52,11 @@ alias cxx14 :
[ run algorithm_reduce_test.cpp ]
[ run algorithm_sum_test.cpp ]
[ run algorithm_empty_test.cpp ]
[ run axis_binary_test.cpp ]
[ run axis_category_test.cpp ]
[ run axis_integer_test.cpp ]
[ run axis_option_test.cpp ]
[ run axis_regular_test.cpp ]
[ run axis_size.cpp ]
[ run axis_traits_test.cpp ]
[ run axis_variable_test.cpp ]
[ run axis_variant_test.cpp ]
@ -136,6 +137,7 @@ alias serialization :
[ run histogram_serialization_test.cpp libserial : $(THIS_PATH) ]
[ run storage_adaptor_serialization_test.cpp libserial : $(THIS_PATH) ]
[ run unlimited_storage_serialization_test.cpp libserial : $(THIS_PATH) ]
: <warnings>off
;
alias libserial :

59
test/axis_binary_test.cpp Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2020 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/axis/binary.hpp>
#include <boost/histogram/axis/ostream.hpp>
#include <limits>
#include <sstream>
#include <type_traits>
#include "std_ostream.hpp"
#include "throw_exception.hpp"
#include "utility_axis.hpp"
#include "utility_str.hpp"
int main() {
using namespace boost::histogram;
BOOST_TEST(std::is_nothrow_move_assignable<axis::binary<>>::value);
BOOST_TEST(std::is_nothrow_move_constructible<axis::binary<>>::value);
// axis::integer with double type
{
axis::binary<> a{"foo"};
BOOST_TEST_EQ(a.metadata(), "foo");
a.metadata() = "bar";
const auto& cref = a;
BOOST_TEST_EQ(cref.metadata(), "bar");
cref.metadata() = "foo";
BOOST_TEST_EQ(cref.metadata(), "foo");
BOOST_TEST_EQ(a.index(true), 1);
BOOST_TEST_EQ(a.index(false), 0);
BOOST_TEST_EQ(a.index(1), 1);
BOOST_TEST_EQ(a.index(0), 0);
BOOST_TEST_CSTR_EQ(str(a).c_str(), "binary(metadata=\"foo\")");
axis::binary<> b;
BOOST_TEST_CSTR_EQ(str(b).c_str(), "binary()");
BOOST_TEST_NE(a, b);
b = a;
BOOST_TEST_EQ(a, b);
axis::binary<> c = std::move(b);
BOOST_TEST_EQ(c, a);
axis::binary<> d;
BOOST_TEST_NE(c, d);
d = std::move(c);
BOOST_TEST_EQ(d, a);
}
// iterators
test_axis_iterator(axis::binary<>(), 0, 2);
return boost::report_errors();
}

View File

@ -82,8 +82,9 @@ int main() {
BOOST_TEST_EQ(a.value(2), C);
BOOST_TEST_THROWS(a.value(3), std::out_of_range);
BOOST_TEST_EQ(str(a),
"category(\"A\", \"B\", \"C\", metadata=\"bar\", options=overflow)");
BOOST_TEST_CSTR_EQ(
str(a).c_str(),
"category(\"A\", \"B\", \"C\", metadata=\"bar\", options=overflow)");
}
// category<int, axis::null_type>: copy, move

View File

@ -127,10 +127,15 @@ int main() {
BOOST_TEST_EQ(a.value(2), 1);
BOOST_TEST_EQ(a.value(3), 2);
BOOST_TEST_EQ(a.index(-2), 1);
BOOST_TEST_EQ(a.index(-1.9), 1);
BOOST_TEST_EQ(a.index(-1.1), 1);
BOOST_TEST_EQ(a.index(-1), 0);
BOOST_TEST_EQ(a.index(0), 1);
BOOST_TEST_EQ(a.index(1), 0);
BOOST_TEST_EQ(a.index(2), 1);
BOOST_TEST_EQ(a.index(2.1), 1);
BOOST_TEST_EQ(a.index(2.9), 1);
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 2);
}

View File

@ -117,7 +117,9 @@ int main() {
BOOST_TEST_THROWS((axis::regular<double, tr::log>{2, -1, 0}), std::invalid_argument);
BOOST_TEST_EQ(str(a), "regular_log(2, 1, 100, options=underflow | overflow)");
BOOST_TEST_CSTR_EQ(
str(a).c_str(),
"regular(transform::log{}, 2, 1, 100, options=underflow | overflow)");
}
// with sqrt transform
@ -139,7 +141,8 @@ int main() {
BOOST_TEST_EQ(a.index(100), 2);
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
BOOST_TEST_EQ(str(a), "regular_sqrt(2, 0, 4, options=underflow | overflow)");
BOOST_TEST_EQ(str(a),
"regular(transform::sqrt{}, 2, 0, 4, options=underflow | overflow)");
}
// with pow transform
@ -162,7 +165,7 @@ int main() {
BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
BOOST_TEST_EQ(str(a),
"regular_pow(2, 0, 4, options=underflow | overflow, power=0.5)");
"regular(transform::pow{0.5}, 2, 0, 4, options=underflow | overflow)");
}
// with step

View File

@ -6,15 +6,13 @@
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <boost/histogram/axis/category.hpp>
#include <boost/histogram/axis/integer.hpp>
#include <boost/histogram/axis.hpp>
#include <boost/histogram/axis/ostream.hpp>
#include <boost/histogram/axis/regular.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/type_name.hpp>
#include <string>
#include <type_traits>
#include <vector>
#include "std_ostream.hpp"
#include "throw_exception.hpp"
#include "utility_allocator.hpp"
#include "utility_axis.hpp"
@ -72,7 +70,7 @@ int main() {
BOOST_TEST_EQ(r1.metadata(), a.metadata());
BOOST_TEST_EQ(r1.options(), a.options());
// change original through r1
axis::get<A>(r1).metadata() = "bar";
r1.metadata() = "bar";
BOOST_TEST_EQ(a.metadata(), "bar");
r1 = &b;
BOOST_TEST_EQ(r1, b);
@ -84,8 +82,8 @@ int main() {
BOOST_TEST_EQ(r2.size(), 3);
BOOST_TEST_EQ(r2.value(0), 1);
BOOST_TEST_EQ(r2.metadata(), "bar");
b.metadata() = "baz";
BOOST_TEST_EQ(r2.metadata(), "baz");
r2.metadata() = "baz"; // change original through r2
BOOST_TEST_EQ(b.metadata(), "baz");
}
// axis::variant copyable
@ -128,8 +126,10 @@ int main() {
BOOST_TEST_CSTR_EQ(str(axis).c_str(), ref);
};
test(axis::regular<>(2, -1, 1, "regular1"),
"regular(2, -1, 1, metadata=\"regular1\", options=underflow | overflow)");
test(axis::regular<>{2, -1, 1, "foo"},
"regular(2, -1, 1, metadata=\"foo\", options=underflow | overflow)");
test(axis::binary<>{"bar"}, "binary(metadata=\"bar\")");
struct user_defined {};
const auto ref = "integer(-1, 1, metadata=" + detail::type_name<user_defined>() +
@ -154,26 +154,27 @@ int main() {
enum { A, B, C };
using variant =
axis::variant<axis::regular<>, axis::regular<double, axis::transform::pow>,
axis::category<>, axis::integer<>>;
axis::category<>, axis::integer<>, axis::binary<>>;
std::vector<variant> axes;
axes.push_back(axis::regular<>{2, -1, 1});
axes.push_back(axis::regular<double, tr::pow>(tr::pow(0.5), 2, 1, 4));
axes.push_back(axis::category<>({A, B, C}));
axes.push_back(axis::regular<double, tr::pow>{tr::pow{0.5}, 2, 1, 4});
axes.push_back(axis::category<>{A, B, C});
axes.push_back(axis::integer<>{-1, 1});
axes.push_back(axis::binary<>{});
for (const auto& a : axes) {
BOOST_TEST(!(a == variant()));
BOOST_TEST_NE(a, variant{});
BOOST_TEST_EQ(a, variant(a));
}
BOOST_TEST_NOT(axes == std::vector<variant>());
BOOST_TEST_NE(axes, std::vector<variant>{});
BOOST_TEST(axes == std::vector<variant>(axes));
}
// axis::variant with axis that has incompatible bin type
{
auto a = axis::variant<axis::category<std::string>>(
axis::category<std::string>({"A", "B", "C"}));
auto a = axis::variant<axis::category<std::string>>{
axis::category<std::string>{"A", "B", "C"}};
BOOST_TEST_THROWS(a.bin(0), std::runtime_error);
auto b = axis::variant<axis::category<int>>(axis::category<int>({2, 1, 3}));
auto b = axis::variant<axis::category<int>>{axis::category<int>{2, 1, 3}};
BOOST_TEST_EQ(b.bin(0), 2);
BOOST_TEST_EQ(b.bin(0).lower(),
b.bin(0).upper()); // lower == upper for bin without interval
@ -191,7 +192,7 @@ int main() {
BOOST_TEST_EQ(axis.index(9), 1);
BOOST_TEST_EQ(axis.size(), 2);
BOOST_TEST_EQ(axis.metadata(), axis::null_type{});
BOOST_TEST_CSTR_EQ(str(axis).c_str(), "<unstreamable>");
BOOST_TEST_EQ(str(axis), detail::type_name<minimal_axis>());
BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
axis = axis::category<std::string>({"A", "B"}, "category");

View File

@ -84,6 +84,12 @@ int main() {
BOOST_TEST_TRAIT_SAME(decltype(category({1, 2}, 0)), category<int, int>);
}
{
using axis::binary;
BOOST_TEST_TRAIT_SAME(decltype(binary{}), binary<null_type>);
BOOST_TEST_TRAIT_SAME(decltype(binary{"foo"}), binary<std::string>);
}
{
auto h = histogram(axis::regular(3, -1, 1), axis::integer(0, 4));
BOOST_TEST_TRAIT_SAME(decltype(h),

View File

@ -19,6 +19,7 @@
#include <boost/histogram/literals.hpp>
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/histogram/unlimited_storage.hpp>
#include <iostream>
#include <ostream>
#include "std_ostream.hpp"
#include "throw_exception.hpp"
@ -104,14 +105,25 @@ int main() {
// counting_streambuf
{
dtl::counting_streambuf<char> cbuf;
std::ostream os(&cbuf);
std::streamsize count = 0;
dtl::counting_streambuf<char> csb(count);
std::ostream os(&csb);
os.put('x');
BOOST_TEST_EQ(cbuf.count, 1);
BOOST_TEST_EQ(count, 1);
os << 12;
BOOST_TEST_EQ(cbuf.count, 3);
BOOST_TEST_EQ(count, 3);
os << "123";
BOOST_TEST_EQ(cbuf.count, 6);
BOOST_TEST_EQ(count, 6);
}
{
std::streamsize count = 0;
auto g = dtl::make_count_guard(std::cout, count);
std::cout.put('x');
BOOST_TEST_EQ(count, 1);
std::cout << 12;
BOOST_TEST_EQ(count, 3);
std::cout << "123";
BOOST_TEST_EQ(count, 6);
}
// sub_array and span

View File

@ -58,7 +58,7 @@ void run_tests() {
" +------------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// regular, narrow
@ -77,12 +77,12 @@ void run_tests() {
" +-----------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h, 40).c_str());
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
// too narrow
BOOST_TEST_CSTR_EQ("BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))END",
str(h, 10).c_str());
BOOST_TEST_CSTR_EQ(str(h, 10).c_str(),
"BEGIN\n"
"histogram(regular(3, -0.5, 1, options=none))END");
}
// regular2
@ -102,7 +102,7 @@ void run_tests() {
" +-------------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// regular with log
@ -111,8 +111,8 @@ void run_tests() {
const auto expected =
"BEGIN\n"
"histogram(regular_log(6, 0.001, 1000, metadata=\"foo\", options=underflow | "
"overflow))\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"
@ -125,7 +125,7 @@ void run_tests() {
" +-----------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// integer
@ -144,7 +144,7 @@ void run_tests() {
" +---------------------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// catorgy<string>
@ -168,7 +168,7 @@ void run_tests() {
" +------------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// histogram with axis that has no value method
@ -184,14 +184,16 @@ void run_tests() {
const auto expected =
"BEGIN\n"
"histogram(<unstreamable>)\n"
"histogram(" +
detail::type_name<minimal_axis>() +
")\n"
" +------------------------------------------------------------------------+\n"
"0 3 |===================================================== |\n"
"1 4 |======================================================================= |\n"
" +------------------------------------------------------------------------+\n"
"END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected.c_str());
}
// fallback for 2D
@ -212,7 +214,7 @@ void run_tests() {
" ( 1 1): 0 (-1 2): nan ( 0 2): 0 ( 1 2): 0 \n"
")END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
// fallback for profile
@ -227,7 +229,7 @@ void run_tests() {
" ( 1): mean(0, 0, -0) \n"
")END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
}
@ -242,7 +244,7 @@ int main() {
const auto expected = "BEGIN\n"
"histogram()END";
BOOST_TEST_CSTR_EQ(expected, str(h).c_str());
BOOST_TEST_CSTR_EQ(str(h).c_str(), expected);
}
return boost::report_errors();