/usr/include/boost/histogram/detail
Edit: /usr/include/boost/histogram/detail/large_int.hpp (6543B)
// 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)
#ifndef BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
#define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace boost {
namespace histogram {
namespace detail {
template
using is_unsigned_integral = mp11::mp_and, std::is_unsigned>;
template
bool safe_increment(T& t) {
if (t < (std::numeric_limits::max)()) {
++t;
return true;
}
return false;
}
template
bool safe_radd(T& t, const U& u) {
static_assert(is_unsigned_integral::value, "T must be unsigned integral type");
static_assert(is_unsigned_integral::value, "T must be unsigned integral type");
if (static_cast((std::numeric_limits::max)() - t) >= u) {
t += static_cast(u); // static_cast to suppress conversion warning
return true;
}
return false;
}
// An integer type which can grow arbitrarily large (until memory is exhausted).
// Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated.
// We use it only to reduce coupling between boost libs.
template
struct large_int : totally_ordered, large_int>,
partially_ordered, void> {
explicit large_int(const Allocator& a = {}) : data(1, 0, a) {}
explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {}
large_int(const large_int&) = default;
large_int& operator=(const large_int&) = default;
large_int(large_int&&) = default;
large_int& operator=(large_int&&) = default;
large_int& operator=(std::uint64_t o) {
data = decltype(data)(1, o);
return *this;
}
large_int& operator++() {
assert(data.size() > 0u);
std::size_t i = 0;
while (!safe_increment(data[i])) {
data[i] = 0;
++i;
if (i == data.size()) {
data.push_back(1);
break;
}
}
return *this;
}
large_int& operator+=(const large_int& o) {
if (this == &o) {
auto tmp{o};
return operator+=(tmp);
}
bool carry = false;
std::size_t i = 0;
for (std::uint64_t oi : o.data) {
auto& di = maybe_extend(i);
if (carry) {
if (safe_increment(oi))
carry = false;
else {
++i;
continue;
}
}
if (!safe_radd(di, oi)) {
add_remainder(di, oi);
carry = true;
}
++i;
}
while (carry) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
large_int& operator+=(std::uint64_t o) {
assert(data.size() > 0u);
if (safe_radd(data[0], o)) return *this;
add_remainder(data[0], o);
// carry the one, data may grow several times
std::size_t i = 1;
while (true) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
explicit operator double() const noexcept {
assert(data.size() > 0u);
double result = static_cast(data[0]);
std::size_t i = 0;
while (++i < data.size())
result += static_cast(data[i]) * std::pow(2.0, i * 64);
return result;
}
bool operator<(const large_int& o) const noexcept {
assert(data.size() > 0u);
assert(o.data.size() > 0u);
// no leading zeros allowed
assert(data.size() == 1 || data.back() > 0u);
assert(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() < o.data.size()) return true;
if (data.size() > o.data.size()) return false;
auto s = data.size();
while (s > 0u) {
--s;
if (data[s] < o.data[s]) return true;
if (data[s] > o.data[s]) return false;
}
return false; // args are equal
}
bool operator==(const large_int& o) const noexcept {
assert(data.size() > 0u);
assert(o.data.size() > 0u);
// no leading zeros allowed
assert(data.size() == 1 || data.back() > 0u);
assert(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() != o.data.size()) return false;
return std::equal(data.begin(), data.end(), o.data.begin());
}
template
std::enable_if_t::value, bool> operator<(const U& o) const
noexcept {
assert(data.size() > 0u);
return data.size() == 1 && safe_less()(data[0], o);
}
template
std::enable_if_t::value, bool> operator>(const U& o) const
noexcept {
assert(data.size() > 0u);
assert(data.size() == 1 || data.back() > 0u); // no leading zeros allowed
return data.size() > 1 || safe_less()(o, data[0]);
}
template
std::enable_if_t::value, bool> operator==(const U& o) const
noexcept {
assert(data.size() > 0u);
return data.size() == 1 && safe_equal()(data[0], o);
}
template
std::enable_if_t::value, bool> operator<(const U& o) const
noexcept {
return operator double() < o;
}
template
std::enable_if_t::value, bool> operator>(const U& o) const
noexcept {
return operator double() > o;
}
template
std::enable_if_t::value, bool> operator==(const U& o) const
noexcept {
return operator double() == o;
}
std::uint64_t& maybe_extend(std::size_t i) {
while (i >= data.size()) data.push_back(0);
return data[i];
}
static void add_remainder(std::uint64_t& d, const std::uint64_t o) noexcept {
assert(d > 0u);
// in decimal system it would look like this:
// 8 + 8 = 6 = 8 - (9 - 8) - 1
// 9 + 1 = 0 = 9 - (9 - 1) - 1
auto tmp = (std::numeric_limits::max)();
tmp -= o;
--d -= tmp;
}
template
void serialize(Archive& ar, unsigned /* version */) {
ar& make_nvp("data", data);
}
std::vector data;
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif