internal operators library replaces duplicated code

This commit is contained in:
Hans Dembinski 2019-06-10 16:05:51 +02:00 committed by GitHub
parent eae2b7fed1
commit 7e40aaf689
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 652 additions and 422 deletions

View File

@ -90,19 +90,22 @@ BOOST_HISTOGRAM_DETECT(is_streamable,
BOOST_HISTOGRAM_DETECT(has_operator_preincrement, (++std::declval<T&>())); BOOST_HISTOGRAM_DETECT(has_operator_preincrement, (++std::declval<T&>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_equal, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_equal,
(std::declval<const T&>() == std::declval<const U&>())); (std::declval<const T&>() == std::declval<const U>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_radd, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_radd,
(std::declval<T&>() += std::declval<U&>())); (std::declval<T&>() += std::declval<U>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rsub, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rsub,
(std::declval<T&>() -= std::declval<U&>())); (std::declval<T&>() -= std::declval<U>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rmul,
(std::declval<T&>() *= std::declval<U&>())); (std::declval<T&>() *= std::declval<U>()));
BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv, BOOST_HISTOGRAM_DETECT_BINARY(has_operator_rdiv,
(std::declval<T&>() /= std::declval<U&>())); (std::declval<T&>() /= std::declval<U>()));
BOOST_HISTOGRAM_DETECT_BINARY(
has_method_eq, (std::declval<const T>().operator==(std::declval<const U>())));
BOOST_HISTOGRAM_DETECT(has_threading_support, (T::has_threading_support)); BOOST_HISTOGRAM_DETECT(has_threading_support, (T::has_threading_support));

View File

@ -8,8 +8,12 @@
#define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP #define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/histogram/detail/static_if.hpp> #include <boost/histogram/detail/operators.hpp>
#include <boost/histogram/detail/safe_comparison.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp> #include <boost/mp11/function.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <cmath> #include <cmath>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
@ -48,7 +52,8 @@ bool safe_radd(T& t, const U& u) {
// Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated. // Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated.
// We use it only to reduce coupling between boost libs. // We use it only to reduce coupling between boost libs.
template <class Allocator> template <class Allocator>
struct large_int { struct large_int : totally_ordered<large_int<Allocator>, large_int<Allocator>>,
partially_ordered<large_int<Allocator>, void> {
explicit large_int(const Allocator& a = {}) : data(1, 0, a) {} explicit large_int(const Allocator& a = {}) : data(1, 0, a) {}
explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {} explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {}
@ -123,7 +128,7 @@ struct large_int {
return *this; return *this;
} }
operator double() const noexcept { explicit operator double() const noexcept {
BOOST_ASSERT(data.size() > 0u); BOOST_ASSERT(data.size() > 0u);
double result = static_cast<double>(data[0]); double result = static_cast<double>(data[0]);
std::size_t i = 0; std::size_t i = 0;
@ -132,7 +137,6 @@ struct large_int {
return result; return result;
} }
// total ordering for large_int, large_int
bool operator<(const large_int& o) const noexcept { bool operator<(const large_int& o) const noexcept {
BOOST_ASSERT(data.size() > 0u); BOOST_ASSERT(data.size() > 0u);
BOOST_ASSERT(o.data.size() > 0u); BOOST_ASSERT(o.data.size() > 0u);
@ -160,77 +164,42 @@ struct large_int {
return std::equal(data.begin(), data.end(), o.data.begin()); return std::equal(data.begin(), data.end(), o.data.begin());
} }
// copied from boost/operators.hpp
friend bool operator>(const large_int& x, const large_int& y) { return y < x; }
friend bool operator<=(const large_int& x, const large_int& y) { return !(y < x); }
friend bool operator>=(const large_int& x, const large_int& y) { return !(x < y); }
friend bool operator!=(const large_int& x, const large_int& y) { return !(x == y); }
// total ordering for large_int, uint64; partial ordering for large_int, double
template <class U> template <class U>
bool operator<(const U& o) const noexcept { std::enable_if_t<std::is_integral<U>::value, bool> operator<(const U& o) const
noexcept {
BOOST_ASSERT(data.size() > 0u); BOOST_ASSERT(data.size() > 0u);
return static_if<is_unsigned_integral<U>>( return data.size() == 1 && safe_less()(data[0], o);
[this](std::uint64_t o) { return data.size() == 1 && data[0] < o; },
[this](double o) { return operator double() < o; }, o);
} }
template <class U> template <class U>
bool operator>(const U& o) const noexcept { std::enable_if_t<std::is_integral<U>::value, bool> operator>(const U& o) const
noexcept {
BOOST_ASSERT(data.size() > 0u); BOOST_ASSERT(data.size() > 0u);
BOOST_ASSERT(data.size() == 1 || data.back() > 0u); // no leading zeros allowed BOOST_ASSERT(data.size() == 1 || data.back() > 0u); // no leading zeros allowed
return static_if<is_unsigned_integral<U>>( return data.size() > 1 || safe_less()(o, data[0]);
[this](std::uint64_t o) { return data.size() > 1 || data[0] > o; },
[this](double o) { return operator double() > o; }, o);
} }
template <class U> template <class U>
bool operator==(const U& o) const noexcept { std::enable_if_t<std::is_integral<U>::value, bool> operator==(const U& o) const
noexcept {
BOOST_ASSERT(data.size() > 0u); BOOST_ASSERT(data.size() > 0u);
return static_if<is_unsigned_integral<U>>( return data.size() == 1 && safe_equal()(data[0], o);
[this](std::uint64_t o) { return data.size() == 1 && data[0] == o; },
[this](double o) { return operator double() == o; }, o);
} }
template <class U> template <class U>
bool operator!=(const U& x) const noexcept { std::enable_if_t<std::is_floating_point<U>::value, bool> operator<(const U& o) const
return !operator==(x); noexcept {
} return operator double() < o;
// adapted copy from boost/operators.hpp
template <class U>
friend bool operator<=(const large_int& x, const U& y) {
if (is_unsigned_integral<U>::value) return !(x > y);
return (x < y) || (x == y);
} }
template <class U> template <class U>
friend bool operator>=(const large_int& x, const U& y) { std::enable_if_t<std::is_floating_point<U>::value, bool> operator>(const U& o) const
if (is_unsigned_integral<U>::value) return !(x < y); noexcept {
return (x > y) || (x == y); return operator double() > o;
} }
template <class U> template <class U>
friend bool operator>(const U& x, const large_int& y) { std::enable_if_t<std::is_floating_point<U>::value, bool> operator==(const U& o) const
return y.operator<(x); noexcept {
} return operator double() == o;
template <class U>
friend bool operator<(const U& x, const large_int& y) {
return y.operator>(x);
}
template <class U>
friend bool operator<=(const U& x, const large_int& y) {
return y >= x;
}
template <class U>
friend bool operator>=(const U& x, const large_int& y) {
return y <= x;
}
template <class U>
friend bool operator==(const U& y, const large_int& x) {
return x.operator==(y);
}
template <class U>
friend bool operator!=(const U& y, const large_int& x) {
return x.operator!=(y);
} }
std::uint64_t& maybe_extend(std::size_t i) { std::uint64_t& maybe_extend(std::size_t i) {
@ -251,12 +220,6 @@ struct large_int {
std::vector<std::uint64_t, Allocator> data; std::vector<std::uint64_t, Allocator> data;
}; };
template <class T>
struct is_large_int : std::false_type {};
template <class Allocator>
struct is_large_int<large_int<Allocator>> : std::true_type {};
} // namespace detail } // namespace detail
} // namespace histogram } // namespace histogram
} // namespace boost } // namespace boost

View File

@ -0,0 +1,135 @@
// Copyright 2019 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_OPERATORS_HPP
#define BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP
#include <boost/histogram/detail/detect.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
using if_not_same_and_has_eq =
std::enable_if_t<(!std::is_same<T, U>::value && has_method_eq<T, U>::value), bool>;
// totally_ordered is for types with a <= b == !(a > b) [floats with NaN violate this]
// Derived must implement <,== for symmetric form and <,>,== for non-symmetric.
// partially_ordered is for types with a <= b == a < b || a == b [for floats with NaN]
// Derived must implement <,== for the symmetric form and <,>,== for non-symmetric.
template <class T, class U>
struct mirrored {
friend bool operator<(const U& a, const T& b) noexcept { return b > a; }
friend bool operator>(const U& a, const T& b) noexcept { return b < a; }
friend bool operator==(const U& a, const T& b) noexcept { return b == a; }
friend bool operator<=(const U& a, const T& b) noexcept { return b >= a; }
friend bool operator>=(const U& a, const T& b) noexcept { return b <= a; }
friend bool operator!=(const U& a, const T& b) noexcept { return b != a; }
};
template <class T>
struct mirrored<T, void> {
template <class U>
friend if_not_same_and_has_eq<T, U> operator<(const U& a, const T& b) noexcept {
return b > a;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator>(const U& a, const T& b) noexcept {
return b < a;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator==(const U& a, const T& b) noexcept {
return b == a;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator<=(const U& a, const T& b) noexcept {
return b >= a;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator>=(const U& a, const T& b) noexcept {
return b <= a;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator!=(const U& a, const T& b) noexcept {
return b != a;
}
};
template <class T>
struct mirrored<T, T> {
friend bool operator>(const T& a, const T& b) noexcept { return b.operator<(a); }
};
template <class T, class U>
struct equality {
friend bool operator!=(const T& a, const U& b) noexcept { return !a.operator==(b); }
};
template <class T>
struct equality<T, void> {
template <class U>
friend if_not_same_and_has_eq<T, U> operator!=(const T& a, const U& b) noexcept {
return !(a == b);
}
};
template <class T, class U>
struct totally_ordered_impl : equality<T, U>, mirrored<T, U> {
friend bool operator<=(const T& a, const U& b) noexcept { return !(a > b); }
friend bool operator>=(const T& a, const U& b) noexcept { return !(a < b); }
};
template <class T>
struct totally_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
template <class U>
friend if_not_same_and_has_eq<T, U> operator<=(const T& a, const U& b) noexcept {
return !(a > b);
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator>=(const T& a, const U& b) noexcept {
return !(a < b);
}
};
template <class T, class... Ts>
using totally_ordered = mp11::mp_rename<
mp11::mp_product<totally_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
mp11::mp_inherit>;
template <class T, class U>
struct partially_ordered_impl : equality<T, U>, mirrored<T, U> {
friend bool operator<=(const T& a, const U& b) noexcept { return a < b || a == b; }
friend bool operator>=(const T& a, const U& b) noexcept { return a > b || a == b; }
};
template <class T>
struct partially_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
template <class U>
friend if_not_same_and_has_eq<T, U> operator<=(const T& a, const U& b) noexcept {
return a < b || a == b;
}
template <class U>
friend if_not_same_and_has_eq<T, U> operator>=(const T& a, const U& b) noexcept {
return a > b || a == b;
}
};
template <class T, class... Ts>
using partially_ordered = mp11::mp_rename<
mp11::mp_product<partially_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
mp11::mp_inherit>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP

View File

@ -7,8 +7,8 @@
#ifndef BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP #ifndef BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP
#define BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP #define BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP
#include <boost/histogram/detail/large_int.hpp>
#include <boost/mp11/utility.hpp> #include <boost/mp11/utility.hpp>
#include <boost/type.hpp>
#include <type_traits> #include <type_traits>
namespace boost { namespace boost {
@ -16,22 +16,18 @@ namespace histogram {
namespace detail { namespace detail {
template <class T> template <class T>
auto make_unsigned(const T t) noexcept { auto make_unsigned(const T& t) noexcept {
static_assert(std::is_integral<T>::value, ""); static_assert(std::is_integral<T>::value, "");
return static_cast<typename std::make_unsigned<T>::type>(t); return static_cast<std::make_unsigned_t<T>>(t);
} }
// clang-format off
template <class T> template <class T>
using number_category = mp11::mp_cond< using number_category =
is_large_int<T>, unsigned, mp11::mp_if<std::is_integral<T>,
std::is_floating_point<T>, float, mp11::mp_if<std::is_signed<T>, type<int>, type<unsigned>>, type<void>>;
std::is_integral<T>, mp11::mp_if<std::is_signed<T>, int, unsigned>,
std::true_type, char>; // do not handle this type
// clang-format on
// version of std::equal_to<> which handles signed, unsigned, large_int, floating // version of std::equal_to<> which handles signed and unsigned integers correctly
struct equal { struct safe_equal {
template <class T, class U> template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept { bool operator()(const T& t, const U& u) const noexcept {
return impl(number_category<T>{}, number_category<U>{}, t, u); return impl(number_category<T>{}, number_category<U>{}, t, u);
@ -42,34 +38,19 @@ struct equal {
return t == u; return t == u;
} }
template <class C, class T, class U>
bool impl(float, C, const T& t, const U& u) const noexcept {
return t == static_cast<T>(u);
}
template <class C, class T, class U>
bool impl(C, float, const T& t, const U& u) const noexcept {
return impl(float{}, C{}, u, t);
}
template <class T, class U> template <class T, class U>
bool impl(float, float, const T& t, const U& u) const noexcept { bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
return t == u;
}
template <class T, class U>
bool impl(int, unsigned, const T& t, const U& u) const noexcept {
return t >= 0 && make_unsigned(t) == u; return t >= 0 && make_unsigned(t) == u;
} }
template <class T, class U> template <class T, class U>
bool impl(unsigned, int, const T& t, const U& u) const noexcept { bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
return impl(int{}, unsigned{}, u, t); return impl(type<int>{}, type<unsigned>{}, u, t);
} }
}; };
// version of std::less<> which handles comparison of signed and unsigned // version of std::less<> which handles signed and unsigned integers correctly
struct less { struct safe_less {
template <class T, class U> template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept { bool operator()(const T& t, const U& u) const noexcept {
return impl(number_category<T>{}, number_category<U>{}, t, u); return impl(number_category<T>{}, number_category<U>{}, t, u);
@ -80,37 +61,22 @@ struct less {
return t < u; return t < u;
} }
template <class C, class T, class U>
bool impl(float, C, const T& t, const U& u) const noexcept {
return t < static_cast<T>(u);
}
template <class C, class T, class U>
bool impl(C, float, const T& t, const U& u) const noexcept {
return static_cast<U>(t) < u;
}
template <class T, class U> template <class T, class U>
bool impl(float, float, const T& t, const U& u) const noexcept { bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
return t < u;
}
template <class T, class U>
bool impl(int, unsigned, const T& t, const U& u) const noexcept {
return t < 0 || make_unsigned(t) < u; return t < 0 || make_unsigned(t) < u;
} }
template <class T, class U> template <class T, class U>
bool impl(unsigned, int, const T& t, const U& u) const noexcept { bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
return 0 < u && t < make_unsigned(u); return 0 < u && t < make_unsigned(u);
} }
}; };
// version of std::greater<> which handles comparison of signed and unsigned // version of std::greater<> which handles signed and unsigned integers correctly
struct greater { struct safe_greater {
template <class T, class U> template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept { bool operator()(const T& t, const U& u) const noexcept {
return less()(u, t); return safe_less()(u, t);
} }
}; };

View File

@ -14,22 +14,28 @@ namespace histogram {
namespace detail { namespace detail {
template <class T, class F, class... Args> template <class T, class F, class... Args>
constexpr decltype(auto) static_if_impl(std::true_type, T&& t, F&&, Args&&... args) { constexpr decltype(auto) static_if_impl(
std::true_type, T&& t, F&&,
Args&&... args) noexcept(noexcept(std::declval<T>()(std::declval<Args>()...))) {
return std::forward<T>(t)(std::forward<Args>(args)...); return std::forward<T>(t)(std::forward<Args>(args)...);
} }
template <class T, class F, class... Args> template <class T, class F, class... Args>
constexpr decltype(auto) static_if_impl(std::false_type, T&&, F&& f, Args&&... args) { constexpr decltype(auto) static_if_impl(
std::false_type, T&&, F&& f,
Args&&... args) noexcept(noexcept(std::declval<F>()(std::declval<Args>()...))) {
return std::forward<F>(f)(std::forward<Args>(args)...); return std::forward<F>(f)(std::forward<Args>(args)...);
} }
template <bool B, class... Ts> template <bool B, class... Ts>
constexpr decltype(auto) static_if_c(Ts&&... ts) { constexpr decltype(auto) static_if_c(Ts&&... ts) noexcept(
noexcept(static_if_impl(std::integral_constant<bool, B>{}, std::declval<Ts>()...))) {
return static_if_impl(std::integral_constant<bool, B>{}, std::forward<Ts>(ts)...); return static_if_impl(std::integral_constant<bool, B>{}, std::forward<Ts>(ts)...);
} }
template <class Bool, class... Ts> template <class Bool, class... Ts>
constexpr decltype(auto) static_if(Ts&&... ts) { constexpr decltype(auto) static_if(Ts&&... ts) noexcept(
noexcept(static_if_impl(Bool{}, std::declval<Ts>()...))) {
return static_if_impl(Bool{}, std::forward<Ts>(ts)...); return static_if_impl(Bool{}, std::forward<Ts>(ts)...);
} }

View File

@ -12,6 +12,7 @@
#include <boost/histogram/detail/attribute.hpp> #include <boost/histogram/detail/attribute.hpp>
#include <boost/histogram/detail/axes.hpp> #include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/iterator_adaptor.hpp> #include <boost/histogram/detail/iterator_adaptor.hpp>
#include <boost/histogram/detail/operators.hpp>
#include <boost/histogram/fwd.hpp> #include <boost/histogram/fwd.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -59,7 +60,7 @@ public:
accessor. Accessors are not copyable. They cannot be stored in containers, but accessor. Accessors are not copyable. They cannot be stored in containers, but
range_iterators can be stored. range_iterators can be stored.
*/ */
class accessor { class accessor : detail::mirrored<accessor, void> {
public: public:
/// Array-like view into the current multi-dimensional index. /// Array-like view into the current multi-dimensional index.
class index_view { class index_view {
@ -165,6 +166,12 @@ public:
} }
// forward all comparison operators to the value // forward all comparison operators to the value
bool operator<(const accessor& o) noexcept { return get() < o.get(); }
bool operator>(const accessor& o) noexcept { return get() > o.get(); }
bool operator==(const accessor& o) noexcept { return get() == o.get(); }
bool operator!=(const accessor& o) noexcept { return get() != o.get(); }
bool operator<=(const accessor& o) noexcept { return get() <= o.get(); }
bool operator>=(const accessor& o) noexcept { return get() >= o.get(); }
template <class U> template <class U>
bool operator<(const U& o) const noexcept { bool operator<(const U& o) const noexcept {
@ -196,60 +203,6 @@ public:
return get() >= o; return get() >= o;
} }
template <class U>
friend bool operator<(const U& x, const accessor& y) noexcept {
return y.operator>(x);
}
template <class U>
friend bool operator>(const U& x, const accessor& y) noexcept {
return y.operator<(x);
}
template <class U>
friend bool operator<=(const U& x, const accessor& y) noexcept {
return y.operator>=(x);
}
template <class U>
friend bool operator>=(const U& x, const accessor& y) noexcept {
return y.operator<=(x);
}
template <class U>
friend bool operator==(const U& x, const accessor& y) noexcept {
return y.operator==(x);
}
template <class U>
friend bool operator!=(const U& x, const accessor& y) noexcept {
return y.operator!=(x);
}
friend bool operator<(const accessor& x, const accessor& y) noexcept {
return x.operator<(y.get());
}
friend bool operator>(const accessor& x, const accessor& y) noexcept {
return x.operator>(y.get());
}
friend bool operator==(const accessor& x, const accessor& y) noexcept {
return x.operator==(y.get());
}
friend bool operator!=(const accessor& x, const accessor& y) noexcept {
return x.operator!=(y.get());
}
friend bool operator<=(const accessor& x, const accessor& y) noexcept {
return x.operator<=(y.get());
}
friend bool operator>=(const accessor& x, const accessor& y) noexcept {
return x.operator>=(y.get());
}
operator value_type() const noexcept { return get(); } operator value_type() const noexcept { return get(); }
private: private:

View File

@ -360,7 +360,7 @@ public:
bool operator==(const U& u) const { bool operator==(const U& u) const {
using std::begin; using std::begin;
using std::end; using std::end;
return std::equal(this->begin(), this->end(), begin(u), end(u), detail::equal{}); return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{});
} }
private: private:

View File

@ -14,6 +14,7 @@
#include <boost/core/exchange.hpp> #include <boost/core/exchange.hpp>
#include <boost/histogram/detail/iterator_adaptor.hpp> #include <boost/histogram/detail/iterator_adaptor.hpp>
#include <boost/histogram/detail/large_int.hpp> #include <boost/histogram/detail/large_int.hpp>
#include <boost/histogram/detail/operators.hpp>
#include <boost/histogram/detail/safe_comparison.hpp> #include <boost/histogram/detail/safe_comparison.hpp>
#include <boost/histogram/detail/static_if.hpp> #include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp> #include <boost/histogram/fwd.hpp>
@ -31,8 +32,7 @@ namespace boost {
namespace histogram { namespace histogram {
namespace detail { namespace detail {
template <class T, class = std::enable_if_t<(std::is_arithmetic<T>::value || template <class T, class = std::enable_if_t<std::is_arithmetic<T>::value>>
is_large_int<T>::value)>>
struct requires_arithmetic {}; struct requires_arithmetic {};
template <class L, class T> template <class L, class T>
@ -249,7 +249,8 @@ public:
class reference; // forward declare to make friend of const_reference class reference; // forward declare to make friend of const_reference
/// implementation detail /// implementation detail
class const_reference { class const_reference
: detail::partially_ordered<const_reference, const_reference, large_int, void> {
public: public:
const_reference(buffer_type& b, std::size_t i) noexcept : bref_(b), idx_(i) { const_reference(buffer_type& b, std::size_t i) noexcept : bref_(b), idx_(i) {
BOOST_ASSERT(idx_ < bref_.size); BOOST_ASSERT(idx_ < bref_.size);
@ -266,94 +267,55 @@ public:
[this](const auto* p) { return static_cast<double>(p[this->idx_]); }); [this](const auto* p) { return static_cast<double>(p[this->idx_]); });
} }
// minimal operators for partial ordering bool operator<(const const_reference& o) const noexcept {
bool operator<(const const_reference& rhs) const noexcept { return apply_binary<detail::safe_less>(o);
return op<detail::less>(rhs);
} }
bool operator<(const large_int& o) const noexcept {
return apply_binary<detail::safe_less>(o);
}
template <class U>
bool operator<(const U& o) const noexcept {
return apply_binary<detail::safe_less>(o);
}
bool operator>(const const_reference& rhs) const noexcept { bool operator>(const const_reference& rhs) const noexcept {
return op<detail::greater>(rhs); return apply_binary<detail::safe_greater>(rhs);
}
bool operator==(const const_reference& rhs) const noexcept {
return op<detail::equal>(rhs);
} }
// adapted copy from boost/operators.hpp for partial ordering bool operator>(const large_int& rhs) const noexcept {
friend bool operator<=(const const_reference& x, const const_reference& y) noexcept { return apply_binary<detail::safe_greater>(rhs);
return !(y < x);
}
friend bool operator>=(const const_reference& x, const const_reference& y) noexcept {
return !(y > x);
}
friend bool operator!=(const const_reference& y, const const_reference& x) noexcept {
return !(x == y);
} }
template <class U, class = detail::requires_arithmetic<U>> template <class U>
bool operator<(const U& rhs) const noexcept { bool operator>(const U& o) const noexcept {
return op<detail::less>(rhs); return apply_binary<detail::safe_greater>(o);
} }
template <class U, class = detail::requires_arithmetic<U>> bool operator==(const const_reference& o) const noexcept {
bool operator>(const U& rhs) const noexcept { return apply_binary<detail::safe_equal>(o);
return op<detail::greater>(rhs);
} }
template <class U, class = detail::requires_arithmetic<U>> bool operator==(const large_int& rhs) const noexcept {
bool operator==(const U& rhs) const noexcept { return apply_binary<detail::safe_equal>(rhs);
return op<detail::equal>(rhs);
} }
// adapted copy from boost/operators.hpp template <class U>
template <class U, class = detail::requires_arithmetic<U>> bool operator==(const U& o) const noexcept {
friend bool operator<=(const const_reference& x, const U& y) noexcept { return apply_binary<detail::safe_equal>(o);
if (detail::is_unsigned_integral<U>::value) return !(x > y);
return (x < y) || (x == y);
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator>=(const const_reference& x, const U& y) noexcept {
if (detail::is_unsigned_integral<U>::value) return !(x < y);
return (x > y) || (x == y);
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator>(const U& x, const const_reference& y) noexcept {
return y < x;
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator<(const U& x, const const_reference& y) noexcept {
return y > x;
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator<=(const U& x, const const_reference& y) noexcept {
if (detail::is_unsigned_integral<U>::value) return !(y < x);
return (y > x) || (y == x);
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator>=(const U& x, const const_reference& y) noexcept {
if (detail::is_unsigned_integral<U>::value) return !(y > x);
return (y < x) || (y == x);
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator==(const U& y, const const_reference& x) noexcept {
return x == y;
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator!=(const U& y, const const_reference& x) noexcept {
return !(x == y);
}
template <class U, class = detail::requires_arithmetic<U>>
friend bool operator!=(const const_reference& y, const U& x) noexcept {
return !(y == x);
} }
private: private:
template <class Binary> template <class Binary>
bool op(const const_reference& x) const noexcept { bool apply_binary(const const_reference& x) const noexcept {
return x.bref_.visit( return x.bref_.visit([this, ix = x.idx_](const auto* xp) {
[this, ix = x.idx_](const auto* xp) { return this->op<Binary>(xp[ix]); }); return this->apply_binary<Binary>(xp[ix]);
});
} }
template <class Binary, class U> template <class Binary, class U>
bool op(const U& x) const noexcept { bool apply_binary(const U& x) const noexcept {
return bref_.visit([i = idx_, &x](const auto* p) { return Binary()(p[i], x); }); return bref_.visit([i = idx_, &x](const auto* p) { return Binary()(p[i], x); });
} }
@ -364,7 +326,9 @@ public:
}; };
/// implementation detail /// implementation detail
class reference : public const_reference { class reference : public const_reference,
public detail::partially_ordered<reference, reference,
const_reference, large_int, void> {
public: public:
reference(buffer_type& b, std::size_t i) noexcept : const_reference(b, i) {} reference(buffer_type& b, std::size_t i) noexcept : const_reference(b, i) {}
@ -377,17 +341,15 @@ public:
} }
reference& operator=(const const_reference& x) { reference& operator=(const const_reference& x) {
x.bref_.visit([this, ix = x.idx_](const auto* xp) { // safe for self-assignment, assigning matching type doesn't invalide buffer
auto xi = xp[ix]; // convert reference to value x.bref_.visit([this, ix = x.idx_](const auto* xp) { this->operator=(xp[ix]); });
this->operator=(xi);
});
return *this; return *this;
} }
template <class U, class = detail::requires_arithmetic<U>> template <class U, class = detail::requires_arithmetic<U>>
reference& operator=(const U& x) { reference& operator=(const U& x) {
this->bref_.visit([this, &x](auto* p) { this->bref_.visit([this, &x](auto* p) {
p[this->idx_] = 0; p[this->idx_] = 0; // LCOV_EXCL_LINE gcc-8 optimizes this away even at -O0
adder()(p, this->bref_, this->idx_, x); adder()(p, this->bref_, this->idx_, x);
}); });
return *this; return *this;
@ -395,12 +357,59 @@ public:
reference& operator=(const large_int& x) { reference& operator=(const large_int& x) {
this->bref_.visit([this, &x](auto* p) { this->bref_.visit([this, &x](auto* p) {
p[this->idx_] = 0; p[this->idx_] = 0; // LCOV_EXCL_LINE gcc-8 optimizes this away even at -O0
adder()(p, this->bref_, this->idx_, x); adder()(p, this->bref_, this->idx_, x);
}); });
return *this; return *this;
} }
bool operator<(const const_reference& o) const noexcept {
return const_reference::operator<(o);
}
bool operator>(const const_reference& o) const noexcept {
return const_reference::operator>(o);
}
bool operator==(const const_reference& o) const noexcept {
return const_reference::operator==(o);
}
bool operator<(const reference& o) const noexcept {
return const_reference::operator<(static_cast<const const_reference&>(o));
}
bool operator==(const reference& o) const noexcept {
return const_reference::operator==(static_cast<const const_reference&>(o));
}
bool operator<(const large_int& o) const noexcept {
return const_reference::operator<(o);
}
bool operator>(const large_int& o) const noexcept {
return const_reference::operator>(o);
}
bool operator==(const large_int& o) const noexcept {
return const_reference::operator==(o);
}
template <class U, class = detail::requires_arithmetic<U>>
bool operator<(const U& o) const noexcept {
return static_cast<const const_reference&>(*this) < o;
}
template <class U, class = detail::requires_arithmetic<U>>
bool operator>(const U& o) const noexcept {
return static_cast<const const_reference&>(*this) > o;
}
template <class U, class = detail::requires_arithmetic<U>>
bool operator==(const U& o) const noexcept {
return static_cast<const const_reference&>(*this) == o;
}
reference& operator+=(const const_reference& x) { reference& operator+=(const const_reference& x) {
x.bref_.visit([this, ix = x.idx_](const auto* xp) { this->operator+=(xp[ix]); }); x.bref_.visit([this, ix = x.idx_](const auto* xp) { this->operator+=(xp[ix]); });
return *this; return *this;
@ -502,7 +511,7 @@ public:
if (size() != x.size()) return false; if (size() != x.size()) return false;
return buffer_.visit([&x](const auto* p) { return buffer_.visit([&x](const auto* p) {
return x.buffer_.visit([p, n = x.size()](const auto* xp) { return x.buffer_.visit([p, n = x.size()](const auto* xp) {
return std::equal(p, p + n, xp, detail::equal{}); return std::equal(p, p + n, xp, detail::safe_equal{});
}); });
}); });
} }
@ -511,7 +520,8 @@ public:
bool operator==(const Iterable& iterable) const { bool operator==(const Iterable& iterable) const {
if (size() != iterable.size()) return false; if (size() != iterable.size()) return false;
return buffer_.visit([&iterable](const auto* p) { return buffer_.visit([&iterable](const auto* p) {
return std::equal(p, p + iterable.size(), std::begin(iterable), detail::equal{}); return std::equal(p, p + iterable.size(), std::begin(iterable),
detail::safe_equal{});
}); });
} }

View File

@ -55,6 +55,8 @@ boost_test(TYPE run SOURCES detail_relaxed_equal_test.cpp
LIBRARIES Boost::histogram Boost::core) LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_replace_default_test.cpp boost_test(TYPE run SOURCES detail_replace_default_test.cpp
LIBRARIES Boost::histogram Boost::core) LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_safe_comparison_test.cpp
LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES detail_tuple_slice_test.cpp boost_test(TYPE run SOURCES detail_tuple_slice_test.cpp
LIBRARIES Boost::histogram Boost::core) LIBRARIES Boost::histogram Boost::core)
boost_test(TYPE run SOURCES histogram_dynamic_test.cpp boost_test(TYPE run SOURCES histogram_dynamic_test.cpp

View File

@ -15,7 +15,6 @@ path-constant THIS_PATH : . ;
project project
: requirements : requirements
<include>$(THIS_PATH)
[ requires [ requires
cxx14_constexpr cxx14_decltype_auto cxx14_generic_lambdas cxx14_return_type_deduction cxx11_user_defined_literals cxx14_constexpr cxx14_decltype_auto cxx14_generic_lambdas cxx14_return_type_deduction cxx11_user_defined_literals
# list could go on... # list could go on...
@ -46,8 +45,10 @@ alias cxx14 :
[ run detail_iterator_adaptor_test.cpp ] [ run detail_iterator_adaptor_test.cpp ]
[ run detail_large_int_test.cpp ] [ run detail_large_int_test.cpp ]
[ run detail_linearize_test.cpp ] [ run detail_linearize_test.cpp ]
[ run detail_operators_test.cpp ]
[ run detail_relaxed_equal_test.cpp ] [ run detail_relaxed_equal_test.cpp ]
[ run detail_replace_default_test.cpp ] [ run detail_replace_default_test.cpp ]
[ run detail_safe_comparison_test.cpp ]
[ run detail_tuple_slice_test.cpp ] [ run detail_tuple_slice_test.cpp ]
[ run histogram_dynamic_test.cpp ] [ run histogram_dynamic_test.cpp ]
[ run histogram_growing_test.cpp ] [ run histogram_growing_test.cpp ]

View File

@ -11,14 +11,21 @@
#include <limits> #include <limits>
#include "std_ostream.hpp" #include "std_ostream.hpp"
using namespace boost::histogram; namespace boost {
namespace histogram {
using large_int = detail::large_int<std::allocator<std::uint64_t>>; namespace detail {
template <class Allocator>
std::ostream& operator<<(std::ostream& os, const large_int& x) { std::ostream& operator<<(std::ostream& os, const large_int<Allocator>& x) {
os << "large_int" << x.data; os << "large_int" << x.data;
return os; return os;
} }
} // namespace detail
} // namespace histogram
} // namespace boost
using namespace boost::histogram;
using large_int = detail::large_int<std::allocator<std::uint64_t>>;
template <class... Ts> template <class... Ts>
auto make_large_int(Ts... ts) { auto make_large_int(Ts... ts) {
@ -64,111 +71,136 @@ int main() {
} }
// comparison // comparison
BOOST_TEST_EQ(large_int(), 0u); {
BOOST_TEST_EQ(large_int(1u), 1u); BOOST_TEST_EQ(large_int(), 0u);
BOOST_TEST_EQ(large_int(1u), 1.0); BOOST_TEST_EQ(large_int(1u), 1u);
BOOST_TEST_EQ(large_int(1u), large_int(1u)); BOOST_TEST_EQ(large_int(1u), 1.0);
BOOST_TEST_NE(large_int(1u), 2u); BOOST_TEST_EQ(large_int(1u), large_int(1u));
BOOST_TEST_NE(large_int(1u), 2.0); BOOST_TEST_NE(large_int(1u), 2u);
BOOST_TEST_NE(large_int(1u), large_int(2u)); BOOST_TEST_NE(large_int(1u), 2.0);
BOOST_TEST_LT(large_int(1u), 2u); BOOST_TEST_NE(large_int(1u), 2.0f);
BOOST_TEST_LT(large_int(1u), 2.0); BOOST_TEST_NE(large_int(1u), large_int(2u));
BOOST_TEST_LT(large_int(1u), large_int(2u)); BOOST_TEST_LT(large_int(1u), 2u);
BOOST_TEST_LE(large_int(1u), 2u); BOOST_TEST_LT(large_int(1u), 2.0);
BOOST_TEST_LE(large_int(1u), 2.0); BOOST_TEST_LT(large_int(1u), 2.0f);
BOOST_TEST_LE(large_int(1u), large_int(2u)); BOOST_TEST_LT(large_int(1u), large_int(2u));
BOOST_TEST_LE(large_int(1u), 1u); BOOST_TEST_LE(large_int(1u), 2u);
BOOST_TEST_GT(large_int(1u), 0u); BOOST_TEST_LE(large_int(1u), 2.0);
BOOST_TEST_GT(large_int(1u), 0.0); BOOST_TEST_LE(large_int(1u), 2.0f);
BOOST_TEST_GT(large_int(1u), large_int(0u)); BOOST_TEST_LE(large_int(1u), large_int(2u));
BOOST_TEST_GE(large_int(1u), 0u); BOOST_TEST_LE(large_int(1u), 1u);
BOOST_TEST_GE(large_int(1u), 0.0); BOOST_TEST_GT(large_int(1u), 0u);
BOOST_TEST_GE(large_int(1u), 1u); BOOST_TEST_GT(large_int(1u), 0.0);
BOOST_TEST_GE(large_int(1u), large_int(0u)); BOOST_TEST_GT(large_int(1u), 0.0f);
BOOST_TEST_NOT(large_int(1u) < large_int(1u)); BOOST_TEST_GT(large_int(1u), large_int(0u));
BOOST_TEST_NOT(large_int(1u) > large_int(1u)); BOOST_TEST_GE(large_int(1u), 0u);
BOOST_TEST_GT(1, large_int()); BOOST_TEST_GE(large_int(1u), 0.0);
BOOST_TEST_LT(-1, large_int()); BOOST_TEST_GE(large_int(1u), 0.0f);
BOOST_TEST_GE(1, large_int()); BOOST_TEST_GE(large_int(1u), 1u);
BOOST_TEST_LE(-1, large_int()); BOOST_TEST_GE(large_int(1u), large_int(0u));
BOOST_TEST_NE(1, large_int()); BOOST_TEST_NOT(large_int(1u) < large_int(1u));
BOOST_TEST_NOT(large_int(1u) > large_int(1u));
BOOST_TEST_GT(1, large_int());
BOOST_TEST_LT(-1, large_int());
BOOST_TEST_GE(1, large_int());
BOOST_TEST_LE(-1, large_int());
BOOST_TEST_NE(1, large_int());
constexpr auto nan = std::numeric_limits<double>::quiet_NaN();
BOOST_TEST_NOT(large_int(1u) < nan);
BOOST_TEST_NOT(large_int(1u) > nan);
BOOST_TEST_NOT(large_int(1u) == nan);
BOOST_TEST(large_int(1u) != nan); // same behavior as int compared to nan
BOOST_TEST_NOT(large_int(1u) <= nan);
BOOST_TEST_NOT(large_int(1u) >= nan);
BOOST_TEST_NOT(nan < large_int(1u));
BOOST_TEST_NOT(nan > large_int(1u));
BOOST_TEST_NOT(nan == large_int(1u));
BOOST_TEST(nan != large_int(1u)); // same behavior as int compared to nan
BOOST_TEST_NOT(nan <= large_int(1u));
BOOST_TEST_NOT(nan >= large_int(1u));
}
// increment // increment
auto a = large_int(); {
++a; auto a = large_int();
BOOST_TEST_EQ(a.data.size(), 1); ++a;
BOOST_TEST_EQ(a.data[0], 1); BOOST_TEST_EQ(a.data.size(), 1);
++a; BOOST_TEST_EQ(a.data[0], 1);
BOOST_TEST_EQ(a.data[0], 2); ++a;
a = vmax; BOOST_TEST_EQ(a.data[0], 2);
BOOST_TEST_EQ(a, vmax); a = vmax;
BOOST_TEST_EQ(a, static_cast<double>(vmax)); BOOST_TEST_EQ(a, vmax);
++a; BOOST_TEST_EQ(a, static_cast<double>(vmax));
BOOST_TEST_EQ(a, make_large_int(0, 1)); ++a;
++a; BOOST_TEST_EQ(a, make_large_int(0, 1));
BOOST_TEST_EQ(a, make_large_int(1, 1)); ++a;
a += a; BOOST_TEST_EQ(a, make_large_int(1, 1));
BOOST_TEST_EQ(a, make_large_int(2, 2));
BOOST_TEST_EQ(a, 2 * static_cast<double>(vmax) + 2);
// carry once A
a.data[0] = vmax;
a.data[1] = 1;
++a;
BOOST_TEST_EQ(a, make_large_int(0, 2));
// carry once B
a.data[0] = vmax;
a.data[1] = 1;
a += 1;
BOOST_TEST_EQ(a, make_large_int(0, 2));
// carry once C
a.data[0] = vmax;
a.data[1] = 1;
a += make_large_int(1, 1);
BOOST_TEST_EQ(a, make_large_int(0, 3));
a.data[0] = vmax - 1;
a.data[1] = vmax;
++a;
BOOST_TEST_EQ(a, make_large_int(vmax, vmax));
// carry two times A
++a;
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry two times B
a = make_large_int(vmax, vmax);
a += 1;
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry two times C
a = make_large_int(vmax, vmax);
a += large_int(1);
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry and enlarge
a = make_large_int(vmax, vmax);
a += a;
BOOST_TEST_EQ(a, make_large_int(vmax - 1, vmax, 1));
// add smaller to larger
a = make_large_int(1, 1, 1);
a += make_large_int(1, 1);
BOOST_TEST_EQ(a, make_large_int(2, 2, 1));
// add larger to smaller
a = make_large_int(1, 1);
a += make_large_int(1, 1, 1);
BOOST_TEST_EQ(a, make_large_int(2, 2, 1));
a = large_int(1);
auto b = 1.0;
BOOST_TEST_EQ(a, b);
for (unsigned i = 0; i < 80; ++i) {
b += b;
BOOST_TEST_NE(a, b);
a += a; a += a;
BOOST_TEST_EQ(a, make_large_int(2, 2));
BOOST_TEST_EQ(a, 2 * static_cast<double>(vmax) + 2);
// carry once A
a.data[0] = vmax;
a.data[1] = 1;
++a;
BOOST_TEST_EQ(a, make_large_int(0, 2));
// carry once B
a.data[0] = vmax;
a.data[1] = 1;
a += 1;
BOOST_TEST_EQ(a, make_large_int(0, 2));
// carry once C
a.data[0] = vmax;
a.data[1] = 1;
a += make_large_int(1, 1);
BOOST_TEST_EQ(a, make_large_int(0, 3));
a.data[0] = vmax - 1;
a.data[1] = vmax;
++a;
BOOST_TEST_EQ(a, make_large_int(vmax, vmax));
// carry two times A
++a;
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry two times B
a = make_large_int(vmax, vmax);
a += 1;
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry two times C
a = make_large_int(vmax, vmax);
a += large_int(1);
BOOST_TEST_EQ(a, make_large_int(0, 0, 1));
// carry and enlarge
a = make_large_int(vmax, vmax);
a += a;
BOOST_TEST_EQ(a, make_large_int(vmax - 1, vmax, 1));
// add smaller to larger
a = make_large_int(1, 1, 1);
a += make_large_int(1, 1);
BOOST_TEST_EQ(a, make_large_int(2, 2, 1));
// add larger to smaller
a = make_large_int(1, 1);
a += make_large_int(1, 1, 1);
BOOST_TEST_EQ(a, make_large_int(2, 2, 1));
a = large_int(1);
auto b = 1.0;
BOOST_TEST_EQ(a, b); BOOST_TEST_EQ(a, b);
for (unsigned i = 0; i < 80; ++i) {
b += b;
BOOST_TEST_NE(a, b);
a += a;
BOOST_TEST_EQ(a, b);
}
BOOST_TEST_GT(a.data.size(), 1u);
} }
BOOST_TEST_GT(a.data.size(), 1u);
return boost::report_errors(); return boost::report_errors();
} }

View File

@ -0,0 +1,141 @@
// Copyright 2019 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/core/lightweight_test_trait.hpp>
#include <boost/histogram/detail/operators.hpp>
#include <limits>
#include <ostream>
using namespace boost::histogram::detail;
struct TotallyOrdered : totally_ordered<TotallyOrdered, TotallyOrdered, void> {
TotallyOrdered(int i) : x(i) {}
bool operator<(const TotallyOrdered& o) const noexcept { return x < o.x; }
bool operator==(const TotallyOrdered& o) const noexcept { return x == o.x; }
bool operator<(const int& o) const noexcept { return x < o; }
bool operator>(const int& o) const noexcept { return x > o; }
bool operator==(const int& o) const noexcept { return x == o; }
int x;
};
std::ostream& operator<<(std::ostream& os, const TotallyOrdered& t) {
os << t.x;
return os;
}
struct PartiallyOrdered : partially_ordered<PartiallyOrdered, PartiallyOrdered, void> {
PartiallyOrdered(double i) : x(i) {}
bool operator<(const PartiallyOrdered& o) const noexcept { return x < o.x; }
bool operator==(const PartiallyOrdered& o) const noexcept { return x == o.x; }
bool operator<(const double& o) const noexcept { return x < o; }
bool operator>(const double& o) const noexcept { return x > o; }
bool operator==(const double& o) const noexcept { return x == o; }
double x;
};
std::ostream& operator<<(std::ostream& os, const PartiallyOrdered& t) {
os << t.x;
return os;
}
template <class T, class U>
void test_nan(std::false_type) {}
template <class T, class U>
void test_nan(std::true_type) {
// the Standard says: any comparison with nan is always false, except != which is always
const U nan = std::numeric_limits<U>::quiet_NaN();
// some of these tests fail on MSVC 19.10 to 19.14
// BOOST_TEST_NOT(nan < nan);
// BOOST_TEST_NOT(nan > nan);
// BOOST_TEST_NOT(nan == nan);
// BOOST_TEST_NOT(nan <= nan);
// BOOST_TEST_NOT(nan >= nan);
// BOOST_TEST(nan != nan);
BOOST_TEST_NOT(T(1.0) < T(nan));
BOOST_TEST_NOT(T(1.0) > T(nan));
BOOST_TEST_NOT(T(1.0) == T(nan));
BOOST_TEST_NOT(T(1.0) <= T(nan));
BOOST_TEST_NOT(T(1.0) >= T(nan));
BOOST_TEST(T(1.0) != T(nan));
BOOST_TEST_NOT(T(nan) < 1.0);
BOOST_TEST_NOT(T(nan) > 1.0);
BOOST_TEST_NOT(T(nan) == 1.0);
BOOST_TEST_NOT(T(nan) <= 1.0);
BOOST_TEST_NOT(T(nan) >= 1.0);
BOOST_TEST(T(nan) != 1.0);
BOOST_TEST_NOT(1.0 < T(nan));
BOOST_TEST_NOT(1.0 > T(nan));
BOOST_TEST_NOT(1.0 == T(nan));
BOOST_TEST_NOT(1.0 <= T(nan));
BOOST_TEST_NOT(1.0 >= T(nan));
BOOST_TEST(1.0 != T(nan));
BOOST_TEST_NOT(T(nan) < T(nan));
BOOST_TEST_NOT(T(nan) > T(nan));
BOOST_TEST_NOT(T(nan) == T(nan));
BOOST_TEST_NOT(T(nan) <= T(nan));
BOOST_TEST_NOT(T(nan) >= T(nan));
BOOST_TEST(T(nan) != T(nan));
BOOST_TEST_NOT(T(nan) < nan);
BOOST_TEST_NOT(T(nan) > nan);
BOOST_TEST_NOT(T(nan) == nan);
BOOST_TEST_NOT(T(nan) <= nan);
BOOST_TEST_NOT(T(nan) >= nan);
BOOST_TEST(T(nan) != nan);
BOOST_TEST_NOT(nan < T(nan));
BOOST_TEST_NOT(nan > T(nan));
BOOST_TEST_NOT(nan == T(nan));
BOOST_TEST_NOT(nan <= T(nan));
BOOST_TEST_NOT(nan >= T(nan));
BOOST_TEST(nan != T(nan));
}
template <class T, class U>
void test() {
T x{1};
U e{1}, l{0}, u{2};
BOOST_TEST_EQ(x, e);
BOOST_TEST_NE(x, l);
BOOST_TEST_LT(x, u);
BOOST_TEST_GT(x, l);
BOOST_TEST_LE(x, e);
BOOST_TEST_LE(x, u);
BOOST_TEST_GE(x, e);
BOOST_TEST_GE(x, l);
BOOST_TEST_EQ(e, x);
BOOST_TEST_NE(l, x);
BOOST_TEST_LT(l, x);
BOOST_TEST_GT(u, x);
BOOST_TEST_LE(e, x);
BOOST_TEST_LE(l, x);
BOOST_TEST_GE(e, x);
BOOST_TEST_GE(u, x);
test_nan<T, U>(std::is_floating_point<U>{});
}
int main() {
test<TotallyOrdered, TotallyOrdered>();
test<TotallyOrdered, int>();
test<PartiallyOrdered, PartiallyOrdered>();
test<PartiallyOrdered, double>();
return boost::report_errors();
}

View File

@ -0,0 +1,60 @@
// Copyright 2018-2019 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/detail/safe_comparison.hpp>
using namespace boost::histogram::detail;
int main() {
auto eq = safe_equal{};
BOOST_TEST(eq(-1, -1));
BOOST_TEST(eq(1, 1u));
BOOST_TEST(eq(1u, 1));
BOOST_TEST(eq(1u, 1u));
BOOST_TEST(eq(1.0, 1));
BOOST_TEST(eq(1, 1.0));
BOOST_TEST(eq(1.0, 1u));
BOOST_TEST(eq(1u, 1.0));
BOOST_TEST_NOT(eq(-1, static_cast<unsigned>(-1)));
BOOST_TEST_NOT(eq(static_cast<unsigned>(-1), -1));
auto lt = safe_less{};
BOOST_TEST(lt(1u, 2u));
BOOST_TEST(lt(-1, 1u));
BOOST_TEST(lt(1u, 2));
BOOST_TEST(lt(-2, -1));
BOOST_TEST(lt(-2.0, -1));
BOOST_TEST(lt(1, 2.0));
BOOST_TEST(lt(-1.0, 1u));
BOOST_TEST(lt(1u, 2.0));
BOOST_TEST(lt(1.0, 2.0));
BOOST_TEST_NOT(lt(1u, 1));
BOOST_TEST_NOT(lt(1, 1u));
BOOST_TEST_NOT(lt(1.0, 1));
BOOST_TEST_NOT(lt(1, 1.0));
BOOST_TEST_NOT(lt(1.0, 1u));
BOOST_TEST_NOT(lt(1u, 1.0));
BOOST_TEST_NOT(lt(1.0, 1.0));
auto gt = safe_greater{};
BOOST_TEST(gt(2u, 1u));
BOOST_TEST(gt(1u, -1));
BOOST_TEST(gt(2, 1u));
BOOST_TEST(gt(-1, -2));
BOOST_TEST(gt(-1, -2.0));
BOOST_TEST(gt(2.0, 1));
BOOST_TEST(gt(1u, -1.0));
BOOST_TEST(gt(2.0, 1u));
BOOST_TEST_NOT(gt(1u, 1));
BOOST_TEST_NOT(gt(1, 1u));
BOOST_TEST_NOT(gt(1.0, 1));
BOOST_TEST_NOT(gt(1, 1.0));
BOOST_TEST_NOT(gt(1.0, 1u));
BOOST_TEST_NOT(gt(1u, 1.0));
return boost::report_errors();
}

View File

@ -20,6 +20,19 @@
#include "throw_exception.hpp" #include "throw_exception.hpp"
#include "utility_allocator.hpp" #include "utility_allocator.hpp"
namespace boost {
namespace histogram {
namespace detail {
template <class Allocator>
std::ostream& operator<<(std::ostream& os, const large_int<Allocator>& x) {
os << "large_int";
os << x.data;
return os;
}
} // namespace detail
} // namespace histogram
} // namespace boost
using namespace boost::histogram; using namespace boost::histogram;
using unlimited_storage_type = unlimited_storage<>; using unlimited_storage_type = unlimited_storage<>;
@ -27,12 +40,6 @@ template <typename T>
using vector_storage = storage_adaptor<std::vector<T>>; using vector_storage = storage_adaptor<std::vector<T>>;
using large_int = unlimited_storage_type::large_int; using large_int = unlimited_storage_type::large_int;
std::ostream& operator<<(std::ostream& os, const large_int& x) {
os << "large_int";
os << x.data;
return os;
}
template <typename T = std::uint8_t> template <typename T = std::uint8_t>
unlimited_storage_type prepare(std::size_t n, T x = T{}) { unlimited_storage_type prepare(std::size_t n, T x = T{}) {
std::unique_ptr<T[]> v(new T[n]); std::unique_ptr<T[]> v(new T[n]);
@ -242,55 +249,6 @@ struct adder {
}; };
int main() { int main() {
// low-level tools
{
auto eq = detail::equal{};
BOOST_TEST(eq(-1, -1));
BOOST_TEST(eq(1, 1u));
BOOST_TEST(eq(1u, 1));
BOOST_TEST(eq(1u, 1u));
BOOST_TEST(eq(1.0, 1));
BOOST_TEST(eq(1, 1.0));
BOOST_TEST(eq(1.0, 1u));
BOOST_TEST(eq(1u, 1.0));
BOOST_TEST_NOT(eq(-1, static_cast<unsigned>(-1)));
BOOST_TEST_NOT(eq(static_cast<unsigned>(-1), -1));
auto lt = detail::less{};
BOOST_TEST(lt(1u, 2u));
BOOST_TEST(lt(-1, 1u));
BOOST_TEST(lt(1u, 2));
BOOST_TEST(lt(-2, -1));
BOOST_TEST(lt(-2.0, -1));
BOOST_TEST(lt(1, 2.0));
BOOST_TEST(lt(-1.0, 1u));
BOOST_TEST(lt(1u, 2.0));
BOOST_TEST(lt(1.0, 2.0));
BOOST_TEST_NOT(lt(1u, 1));
BOOST_TEST_NOT(lt(1, 1u));
BOOST_TEST_NOT(lt(1.0, 1));
BOOST_TEST_NOT(lt(1, 1.0));
BOOST_TEST_NOT(lt(1.0, 1u));
BOOST_TEST_NOT(lt(1u, 1.0));
BOOST_TEST_NOT(lt(1.0, 1.0));
auto gt = detail::greater{};
BOOST_TEST(gt(2u, 1u));
BOOST_TEST(gt(1u, -1));
BOOST_TEST(gt(2, 1u));
BOOST_TEST(gt(-1, -2));
BOOST_TEST(gt(-1, -2.0));
BOOST_TEST(gt(2.0, 1));
BOOST_TEST(gt(1u, -1.0));
BOOST_TEST(gt(2.0, 1u));
BOOST_TEST_NOT(gt(1u, 1));
BOOST_TEST_NOT(gt(1, 1u));
BOOST_TEST_NOT(gt(1.0, 1));
BOOST_TEST_NOT(gt(1, 1.0));
BOOST_TEST_NOT(gt(1.0, 1u));
BOOST_TEST_NOT(gt(1u, 1.0));
}
// empty state // empty state
{ {
unlimited_storage_type a; unlimited_storage_type a;