mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-11 13:14:06 +00:00
improved support and testing of axes over boost::units
This commit is contained in:
parent
68ce22a323
commit
2d5fb32b88
@ -30,27 +30,24 @@ namespace axis {
|
||||
result in a dangling reference. Rather than specialing the code to handle
|
||||
this, it seems easier to just use a value instead of a view here.
|
||||
*/
|
||||
template <typename T>
|
||||
template <typename RealType>
|
||||
class polymorphic_bin {
|
||||
using value_type = T;
|
||||
using value_type = RealType;
|
||||
|
||||
public:
|
||||
polymorphic_bin(value_type value)
|
||||
: lower_or_value_(value), upper_(value), center_(value) {}
|
||||
|
||||
polymorphic_bin(value_type lower, value_type upper, value_type center)
|
||||
: lower_or_value_(lower), upper_(upper), center_(center) {}
|
||||
polymorphic_bin(value_type lower, value_type upper)
|
||||
: lower_or_value_(lower), upper_(upper) {}
|
||||
|
||||
operator value_type() const noexcept { return lower_or_value_; }
|
||||
|
||||
value_type lower() const noexcept { return lower_or_value_; }
|
||||
value_type upper() const noexcept { return upper_; }
|
||||
value_type center() const noexcept { return center_; }
|
||||
value_type center() const noexcept { return 0.5 * (lower() + upper()); }
|
||||
value_type width() const noexcept { return upper() - lower(); }
|
||||
|
||||
template <typename BinType>
|
||||
bool operator==(const BinType& rhs) const noexcept {
|
||||
return equal_impl(rhs, detail::has_method_lower<BinType>());
|
||||
return equal_impl(detail::has_method_lower<BinType>(), rhs);
|
||||
}
|
||||
|
||||
template <typename BinType>
|
||||
@ -61,22 +58,21 @@ public:
|
||||
bool is_discrete() const noexcept { return lower_or_value_ == upper_; }
|
||||
|
||||
private:
|
||||
bool equal_impl(const polymorphic_bin& rhs, std::true_type) const noexcept {
|
||||
return lower_or_value_ == rhs.lower_or_value_ && upper_ == rhs.upper_ &&
|
||||
center_ == rhs.center_;
|
||||
bool equal_impl(std::true_type, const polymorphic_bin& rhs) const noexcept {
|
||||
return lower_or_value_ == rhs.lower_or_value_ && upper_ == rhs.upper_;
|
||||
}
|
||||
|
||||
template <typename BinType>
|
||||
bool equal_impl(const BinType& rhs, std::true_type) const noexcept {
|
||||
bool equal_impl(std::true_type, const BinType& rhs) const noexcept {
|
||||
return lower() == rhs.lower() && upper() == rhs.upper();
|
||||
}
|
||||
|
||||
template <typename BinType>
|
||||
bool equal_impl(const BinType& rhs, std::false_type) const noexcept {
|
||||
bool equal_impl(std::false_type, const BinType& rhs) const noexcept {
|
||||
return is_discrete() && static_cast<value_type>(*this) == rhs;
|
||||
}
|
||||
|
||||
const value_type lower_or_value_, upper_, center_;
|
||||
const value_type lower_or_value_, upper_;
|
||||
};
|
||||
|
||||
} // namespace axis
|
||||
|
@ -21,27 +21,6 @@
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
using get_value_type = typename T::value_type;
|
||||
|
||||
template <typename T>
|
||||
using get_unit_type = typename T::unit_type;
|
||||
|
||||
struct one {};
|
||||
|
||||
template <typename T>
|
||||
T operator*(T&& t, const one&) {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T operator/(T&& t, const one&) {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
|
||||
// two_pi can be found in boost/math, but it is defined here to reduce deps
|
||||
@ -115,9 +94,8 @@ class regular : public base<MetaData, Options>,
|
||||
using metadata_type = MetaData;
|
||||
using transform_type = Transform;
|
||||
using value_type = RealType;
|
||||
using unit_type = detail::mp_eval_or<detail::get_unit_type, value_type, detail::one>;
|
||||
using internal_type =
|
||||
detail::mp_eval_or<detail::get_value_type, value_type, value_type>;
|
||||
using unit_type = detail::get_unit_type<value_type>;
|
||||
using internal_type = detail::get_scale_type<value_type>;
|
||||
|
||||
static_assert(!(Options & option_type::circular) || !(Options & option_type::underflow),
|
||||
"circular axis cannot have underflow");
|
||||
@ -136,8 +114,8 @@ public:
|
||||
metadata_type m = {})
|
||||
: base_type(n, std::move(m))
|
||||
, transform_type(std::move(trans))
|
||||
, min_(this->forward(mag(start)))
|
||||
, delta_(this->forward(mag(stop)) - min_) {
|
||||
, min_(this->forward(detail::get_scale(start)))
|
||||
, delta_(this->forward(detail::get_scale(stop)) - min_) {
|
||||
if (!std::isfinite(min_) || !std::isfinite(delta_))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("forward transform of start or stop invalid"));
|
||||
@ -160,8 +138,8 @@ public:
|
||||
regular(const regular& src, int begin, int end, unsigned merge)
|
||||
: base_type((end - begin) / merge, src.metadata())
|
||||
, transform_type(src.transform())
|
||||
, min_(this->forward(mag(src.value(begin))))
|
||||
, delta_(this->forward(mag(src.value(end))) - min_) {
|
||||
, min_(this->forward(detail::get_scale(src.value(begin))))
|
||||
, delta_(this->forward(detail::get_scale(src.value(end))) - min_) {
|
||||
BOOST_ASSERT((end - begin) % merge == 0);
|
||||
if (Options & option_type::circular && !(begin == 0 && end == src.size()))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
|
||||
@ -222,8 +200,6 @@ public:
|
||||
void serialize(Archive&, unsigned);
|
||||
|
||||
private:
|
||||
internal_type mag(const value_type& x) const noexcept { return x / unit_type(); }
|
||||
|
||||
internal_type min_, delta_;
|
||||
}; // namespace axis
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
#ifndef BOOST_HISTOGRAM_AXIS_TRAITS_HPP
|
||||
#define BOOST_HISTOGRAM_AXIS_TRAITS_HPP
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/detail/cat.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram_fwd.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
@ -24,6 +26,42 @@ template <typename T>
|
||||
axis::option_type options_impl(const T& t, std::true_type) {
|
||||
return t.options();
|
||||
}
|
||||
|
||||
template <typename FIntArg, typename FDoubleArg, typename T>
|
||||
decltype(auto) value_method_switch(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
|
||||
using U = unqual<T>;
|
||||
return static_if<has_method_value<U>>(
|
||||
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) {
|
||||
using A = unqual<decltype(t)>;
|
||||
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
},
|
||||
[](FIntArg&&, FDoubleArg&&, const auto& t) {
|
||||
using A = unqual<decltype(t)>;
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(A)), " has no value method")));
|
||||
return 0;
|
||||
},
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
}
|
||||
|
||||
template <typename R1, typename R2, typename FIntArg, typename FDoubleArg, typename T>
|
||||
R2 value_method_switch_with_return_type(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
|
||||
using U = unqual<T>;
|
||||
return static_if<has_method_value_with_convertible_return_type<U, R1>>(
|
||||
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) -> R2 {
|
||||
using A = unqual<decltype(t)>;
|
||||
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
},
|
||||
[](FIntArg&&, FDoubleArg&&, const auto&) -> R2 {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
" has no value method or return type is not convertible to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(R1)))));
|
||||
},
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
namespace axis {
|
||||
@ -51,37 +89,31 @@ int extend(const T& t) noexcept {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
double value(const T& t, double idx) {
|
||||
return detail::static_if<detail::has_method_value<detail::unqual<T>, double>>(
|
||||
[&](const auto& a) {
|
||||
using Arg = detail::unqual<detail::arg_type<detail::unqual<decltype(a)>>>;
|
||||
return detail::static_if<std::is_integral<Arg>>(
|
||||
[&](const auto& a) -> double { return a.value(static_cast<int>(idx)); },
|
||||
[&](const auto& a) -> double { return a.value(idx); }, a);
|
||||
},
|
||||
[](const auto&) -> double {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
"axis has no value method or return type is not convertible to double"));
|
||||
return 0;
|
||||
},
|
||||
t);
|
||||
decltype(auto) value(const T& t, double idx) {
|
||||
return detail::value_method_switch(
|
||||
[idx](const auto& a) { return a.value(static_cast<int>(idx)); },
|
||||
[idx](const auto& a) { return a.value(idx); }, t);
|
||||
}
|
||||
|
||||
template <typename R, typename T>
|
||||
R value_as(const T& t, double idx) {
|
||||
return detail::value_method_switch_with_return_type<R, R>(
|
||||
[idx](const auto& a) -> R { return a.value(static_cast<int>(idx)); },
|
||||
[idx](const auto& a) -> R { return a.value(idx); }, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
double width(const T& t, int idx) {
|
||||
return detail::static_if<detail::has_method_value<detail::unqual<T>, double>>(
|
||||
[&](const auto& a) {
|
||||
using Arg = detail::unqual<detail::arg_type<detail::unqual<decltype(a)>>>;
|
||||
return detail::static_if<std::is_integral<Arg>>(
|
||||
[&](const auto&) -> double { return 1; },
|
||||
[&](const auto& a) -> double { return a.value(idx + 1) - a.value(idx); }, a);
|
||||
},
|
||||
[](const auto&) -> double {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
"axis has no value method or return type is not convertible to double"));
|
||||
return 0;
|
||||
},
|
||||
t);
|
||||
decltype(auto) width(const T& t, int idx) {
|
||||
return detail::value_method_switch(
|
||||
[](const auto&) { return 0; },
|
||||
[idx](const auto& a) { return a.value(idx + 1) - a.value(idx); }, t);
|
||||
}
|
||||
|
||||
template <typename R, typename T>
|
||||
R width_as(const T& t, int idx) {
|
||||
return detail::value_method_switch_with_return_type<R, R>(
|
||||
[](const auto&) { return R(); },
|
||||
[idx](const auto& a) -> R { return a.value(idx + 1) - a.value(idx); }, t);
|
||||
}
|
||||
|
||||
} // namespace traits
|
||||
|
@ -27,42 +27,6 @@ namespace boost {
|
||||
namespace histogram {
|
||||
|
||||
namespace detail {
|
||||
struct get_polymorphic_bin : public boost::static_visitor<axis::polymorphic_bin<double>> {
|
||||
using T = axis::polymorphic_bin<double>;
|
||||
int idx;
|
||||
get_polymorphic_bin(int i) : idx(i) {}
|
||||
|
||||
template <typename A>
|
||||
T operator()(const A& a) const {
|
||||
// using static_if produces internal compiler error in gcc-5.5 here
|
||||
return impl(a, detail::has_method_value<A, double>());
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
T impl(const A&, std::false_type) const {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
cat(boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
|
||||
" has no value method with return type convertible to double")));
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
T impl(const A& a, std::true_type) const {
|
||||
using Arg = detail::unqual<detail::arg_type<detail::unqual<A>>>;
|
||||
return impl(a, std::true_type(), std::is_floating_point<Arg>());
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
T impl(const A& a, std::true_type, std::false_type) const {
|
||||
return T(a.value(idx));
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
T impl(const A& a, std::true_type, std::true_type) const {
|
||||
return T(a.value(idx), a.value(idx + 1), a.value(idx + 0.5));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename R>
|
||||
struct functor_wrapper : public boost::static_visitor<R> {
|
||||
F& fcn;
|
||||
@ -201,13 +165,26 @@ public:
|
||||
// Only works for axes with value method that returns something convertible to
|
||||
// double and will throw a runtime_error otherwise, see axis::traits::value
|
||||
double value(double idx) const {
|
||||
return visit([idx](const auto& a) { return axis::traits::value(a, idx); }, *this);
|
||||
return visit([idx](const auto& a) { return traits::value_as<double>(a, idx); },
|
||||
*this);
|
||||
}
|
||||
|
||||
auto operator[](const int idx) const {
|
||||
// using visit here causes internal error in MSVC 2017, so we work around
|
||||
return boost::apply_visitor(detail::get_polymorphic_bin(idx),
|
||||
static_cast<const base_type&>(*this));
|
||||
return visit(
|
||||
[idx](const auto& a) {
|
||||
return detail::value_method_switch_with_return_type<double,
|
||||
polymorphic_bin<double>>(
|
||||
[idx](const auto& a) { // axis is discrete
|
||||
const auto x = a.value(idx);
|
||||
return polymorphic_bin<double>(x, x);
|
||||
},
|
||||
[idx](const auto& a) { // axis is continuous
|
||||
return polymorphic_bin<double>(a.value(idx), a.value(idx + 1));
|
||||
},
|
||||
a);
|
||||
},
|
||||
*this);
|
||||
}
|
||||
|
||||
template <typename... Us>
|
||||
@ -313,14 +290,6 @@ const T* get(const U* u) {
|
||||
return std::is_same<T, detail::unqual<U>>::value ? reinterpret_cast<const T*>(u)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
// pass-through version for generic programming, if U is axis instead of variant
|
||||
template <typename Functor, typename T,
|
||||
typename = detail::requires_axis<detail::unqual<T>>>
|
||||
decltype(auto) visit(Functor&& f, T&& t) {
|
||||
return std::forward<Functor>(f)(std::forward<T>(t));
|
||||
}
|
||||
|
||||
} // namespace axis
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -155,14 +155,25 @@ void rank_check(const T& axes, const unsigned N) {
|
||||
BOOST_ASSERT_MSG(N < axes_size(axes), "index out of range");
|
||||
}
|
||||
|
||||
template <typename F, typename... Ts>
|
||||
void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
|
||||
mp11::tuple_for_each(axes, std::forward<F>(f));
|
||||
template <typename F, typename T>
|
||||
void for_each_axis_impl(std::true_type, const T& axes, F&& f) {
|
||||
for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); }
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
void for_each_axis_impl(std::false_type, const T& axes, F&& f) {
|
||||
for (const auto& x : axes) f(x);
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
void for_each_axis(const T& axes, F&& f) {
|
||||
for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); }
|
||||
using U = mp11::mp_first<unqual<T>>;
|
||||
for_each_axis_impl(is_axis_variant<U>(), axes, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename F, typename... Ts>
|
||||
void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
|
||||
mp11::tuple_for_each(axes, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Axes, typename T>
|
||||
@ -172,8 +183,8 @@ using axes_buffer = boost::container::static_vector<
|
||||
std::tuple_size, Axes>::value>;
|
||||
|
||||
template <typename T>
|
||||
auto make_empty_axes(const std::vector<T>& t) {
|
||||
auto r = std::vector<T>(t.get_allocator());
|
||||
auto make_empty_axes(const T& t) {
|
||||
auto r = T(t.get_allocator());
|
||||
r.reserve(t.size());
|
||||
for_each_axis(t, [&r](const auto& a) {
|
||||
using U = unqual<decltype(a)>;
|
||||
@ -199,9 +210,9 @@ auto make_sub_axes(const std::tuple<Ts...>& t, Ns... ns) {
|
||||
return std::make_tuple(std::get<ns>(t)...);
|
||||
}
|
||||
|
||||
template <typename... Ns, typename... Ts>
|
||||
auto make_sub_axes(const std::vector<Ts...>& t, Ns... ns) {
|
||||
return std::vector<Ts...>({t[ns]...}, t.get_allocator());
|
||||
template <typename... Ns, typename T>
|
||||
auto make_sub_axes(const T& t, Ns... ns) {
|
||||
return T({t[ns]...}, t.get_allocator());
|
||||
}
|
||||
|
||||
/// Index with an invalid state
|
||||
|
@ -172,27 +172,17 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_clear, &T::clear);
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, &T::lower);
|
||||
|
||||
template <typename T, typename X>
|
||||
struct has_method_value_impl {
|
||||
template <typename U, typename V = decltype(std::declval<U&>().value(0))>
|
||||
static typename std::is_convertible<V, X>::type Test(void*);
|
||||
template <typename U>
|
||||
static std::false_type Test(...);
|
||||
using type = decltype(Test<T>(nullptr));
|
||||
};
|
||||
template <typename T, typename X>
|
||||
using has_method_value = typename has_method_value_impl<T, X>::type;
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, &T::value);
|
||||
|
||||
template <typename T>
|
||||
struct has_method_options_impl {
|
||||
template <typename U, typename V = decltype(std::declval<const U&>().options())>
|
||||
static typename std::is_same<V, axis::option_type>::type Test(void*);
|
||||
template <typename U>
|
||||
static std::false_type Test(...);
|
||||
using type = decltype(Test<T>(nullptr));
|
||||
};
|
||||
template <typename T>
|
||||
using has_method_options = typename has_method_options_impl<T>::type;
|
||||
using get_value_method_return_type_impl = decltype(std::declval<T&>().value(0));
|
||||
|
||||
template <typename T, typename R>
|
||||
using has_method_value_with_convertible_return_type =
|
||||
typename std::is_convertible<mp_eval_or<get_value_method_return_type_impl, T, void>,
|
||||
R>::type;
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, (std::declval<const T&>().options()));
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_allocator, &T::get_allocator);
|
||||
|
||||
@ -318,6 +308,45 @@ template <typename T>
|
||||
constexpr bool relaxed_equal(const T& a, const T& b) noexcept {
|
||||
return relaxed_equal_impl(is_equal_comparable<unqual<T>>(), a, b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using get_scale_type_helper = typename T::value_type;
|
||||
|
||||
template <typename T>
|
||||
using get_scale_type = detail::mp_eval_or<detail::get_scale_type_helper, T, T>;
|
||||
|
||||
struct one_unit {};
|
||||
|
||||
template <typename T>
|
||||
T operator*(T&& t, const one_unit&) {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T operator/(T&& t, const one_unit&) {
|
||||
return std::forward<T>(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using get_unit_type_helper = typename T::unit_type;
|
||||
|
||||
template <typename T>
|
||||
using get_unit_type = detail::mp_eval_or<detail::get_unit_type_helper, T, one_unit>;
|
||||
|
||||
template <typename T, typename R = get_scale_type<T>>
|
||||
R get_scale(const T& t) {
|
||||
return t / get_unit_type<T>();
|
||||
}
|
||||
|
||||
struct product {
|
||||
auto operator()() { return 1.0; }
|
||||
|
||||
template <class T, class... Ts>
|
||||
auto operator()(T t, Ts... ts) {
|
||||
return t * product()(ts...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -59,11 +59,13 @@ public:
|
||||
|
||||
decltype(auto) bin(unsigned d) const { return parent_.hist_.axis(d)[(*this)[d]]; }
|
||||
|
||||
decltype(auto) density() const {
|
||||
double density() const {
|
||||
double x = 1;
|
||||
auto it = begin();
|
||||
parent_.hist_.for_each_axis(
|
||||
[&](const auto& a) { x *= axis::traits::width(a, *it++); });
|
||||
parent_.hist_.for_each_axis([&](const auto& a) {
|
||||
const auto w = axis::traits::width_as<double>(a, *it++);
|
||||
x *= w ? w : 1;
|
||||
});
|
||||
return *iter_ / x;
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,7 @@ int main() {
|
||||
BOOST_TEST_EQ(db.at<long>().first, 5u);
|
||||
}
|
||||
|
||||
// testing pass-through versions of get and visit
|
||||
// testing pass-through versions of get
|
||||
{
|
||||
axis::regular<> a(10, 0, 1);
|
||||
axis::integer<> b(0, 3);
|
||||
@ -251,8 +251,6 @@ int main() {
|
||||
BOOST_TEST_EQ(tb, &b);
|
||||
const auto* tc = axis::get<axis::regular<>>(&b);
|
||||
BOOST_TEST_EQ(tc, nullptr);
|
||||
|
||||
axis::visit([&](const auto& x) { BOOST_TEST_EQ(a, x); }, a);
|
||||
}
|
||||
|
||||
// iterators
|
||||
|
@ -51,13 +51,14 @@ int main() {
|
||||
BOOST_TEST_EQ(b.value(3) / si::meter, std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
// histogram with quanity axis
|
||||
// histogram with quantity axis
|
||||
{
|
||||
auto h = make_histogram(axis::regular<Q>(2, 0 * si::meter, 1 * si::meter),
|
||||
axis::regular<>(2, 0, 1));
|
||||
h(0.1 * si::meter, 0.1); // fills bin (0, 0)
|
||||
BOOST_TEST_EQ(h.at(0, 0), 1);
|
||||
for (auto&& x : indexed(h)) {
|
||||
// auto d = x.density(); // NEEDS TO BE FIXED AT LEAST FOR STATIC AXIS
|
||||
// BOOST_TEST_EQ(d * si::meter, 0.25);
|
||||
BOOST_TEST_THROWS(x.density(), std::runtime_error); // cannot use density method
|
||||
BOOST_TEST_EQ(x[0], 2.0 * x.bin(0_c).lower() / si::meter);
|
||||
BOOST_TEST_EQ(x[1], 2.0 * x.bin(1_c).lower());
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ struct VisitorTestFunctor {
|
||||
};
|
||||
|
||||
int main() {
|
||||
// has_method_value
|
||||
// has_method_value*
|
||||
{
|
||||
struct A {};
|
||||
struct B {
|
||||
@ -48,11 +48,15 @@ int main() {
|
||||
char value(int) const { return 0; }
|
||||
};
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<A, double>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<B, A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<B, char>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<C, char>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<C, A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value<A>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<B>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value<C>));
|
||||
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value_with_convertible_return_type<A, double>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value_with_convertible_return_type<B, A>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value_with_convertible_return_type<B, char>));
|
||||
BOOST_TEST_TRAIT_TRUE((has_method_value_with_convertible_return_type<C, int>));
|
||||
BOOST_TEST_TRAIT_FALSE((has_method_value_with_convertible_return_type<C, A>));
|
||||
}
|
||||
|
||||
// has_method_options
|
||||
|
Loading…
x
Reference in New Issue
Block a user