mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-10 07:14:05 +00:00
internal operators library replaces duplicated code
This commit is contained in:
parent
eae2b7fed1
commit
7e40aaf689
@ -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));
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
135
include/boost/histogram/detail/operators.hpp
Normal file
135
include/boost/histogram/detail/operators.hpp
Normal 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
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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{});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 ]
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
141
test/detail_operators_test.cpp
Normal file
141
test/detail_operators_test.cpp
Normal 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();
|
||||||
|
}
|
60
test/detail_safe_comparison_test.cpp
Normal file
60
test/detail_safe_comparison_test.cpp
Normal 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();
|
||||||
|
}
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user