* Use uint64_t instead of size_t as hash return value * Expect alignof(uint64_t) <= 8 instead of == 8
197 lines
4.1 KiB
C++
197 lines
4.1 KiB
C++
#pragma once
|
|
|
|
#include <psemek/util/span.hpp>
|
|
#include <psemek/util/range.hpp>
|
|
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <concepts>
|
|
|
|
namespace psemek::util
|
|
{
|
|
|
|
struct big_int
|
|
{
|
|
using digit = std::uint32_t;
|
|
|
|
big_int() = default;
|
|
|
|
template <std::integral T>
|
|
big_int(T value);
|
|
|
|
big_int(big_int const &) = delete;
|
|
|
|
big_int(big_int && other);
|
|
|
|
static big_int from_digits(std::unique_ptr<digit[]> digits, std::uint32_t size, bool negative = false);
|
|
|
|
template <typename Iterator>
|
|
static big_int from_digits(Iterator begin, Iterator end, bool negative = false);
|
|
|
|
template <typename Container>
|
|
static big_int from_digits(Container && container, bool negative = false);
|
|
|
|
big_int & operator = (big_int const &) = delete;
|
|
|
|
big_int & operator = (big_int && other);
|
|
|
|
util::span<digit> digits();
|
|
util::span<digit const> digits() const;
|
|
|
|
bool negative() const;
|
|
|
|
private:
|
|
static constexpr std::uint32_t sign_bit = std::uint32_t(1) << 31;
|
|
static constexpr std::uint32_t digit_size = sizeof(digit);
|
|
static constexpr std::uint32_t digit_bits = digit_size * 8;
|
|
static constexpr std::uint32_t digit_mask = digit(-1);
|
|
|
|
std::unique_ptr<digit[]> digits_ = nullptr;
|
|
// Highest bit of size stores the overall big int sign
|
|
std::uint32_t size_ = 0;
|
|
std::uint32_t capacity_ = 0;
|
|
|
|
void set_negative(bool negative)
|
|
{
|
|
size_ = size() | (negative ? sign_bit : 0);
|
|
}
|
|
|
|
std::uint32_t size() const
|
|
{
|
|
return size_ & (~sign_bit);
|
|
}
|
|
|
|
void set_size(std::uint32_t new_size)
|
|
{
|
|
size_ = new_size | (size_ & sign_bit);
|
|
}
|
|
|
|
void canonicalize();
|
|
|
|
void ensure_capacity(std::uint32_t new_capacity);
|
|
|
|
template <std::integral T>
|
|
static T shift_by_digit(T value);
|
|
|
|
template <std::integral T>
|
|
static std::uint32_t size_for(T value);
|
|
|
|
void add_positive(span<digit const> digits);
|
|
};
|
|
|
|
template <std::integral T>
|
|
big_int::big_int(T value)
|
|
{
|
|
auto size = size_for(value);
|
|
ensure_capacity(size);
|
|
set_size(size);
|
|
set_negative(value < 0);
|
|
for (digit & d : digits())
|
|
{
|
|
d = value & digit_mask;
|
|
value = shift_by_digit(value);
|
|
}
|
|
|
|
if (negative())
|
|
{
|
|
for (digit & d : digits())
|
|
d = ~d;
|
|
digit const one = 1;
|
|
add_positive(make_singleton_span(one));
|
|
}
|
|
}
|
|
|
|
inline big_int::big_int(big_int && other)
|
|
: digits_(std::move(other.digits_))
|
|
, size_(other.size_)
|
|
, capacity_(other.capacity_)
|
|
{
|
|
other.size_ = 0;
|
|
other.capacity_ = 0;
|
|
}
|
|
|
|
inline big_int big_int::from_digits(std::unique_ptr<digit[]> digits, std::uint32_t size, bool negative)
|
|
{
|
|
big_int result;
|
|
result.digits_ = std::move(digits);
|
|
result.size_ = size;
|
|
result.capacity_ = size;
|
|
result.set_negative(negative);
|
|
result.canonicalize();
|
|
return result;
|
|
}
|
|
|
|
template <typename Iterator>
|
|
big_int big_int::from_digits(Iterator begin, Iterator end, bool negative)
|
|
{
|
|
auto size = std::distance(begin, end);
|
|
auto digits = std::make_unique<digit[]>(size);
|
|
std::copy(begin, end, digits.get());
|
|
return from_digits(std::move(digits), size, negative);
|
|
}
|
|
|
|
template <typename Container>
|
|
big_int big_int::from_digits(Container && container, bool negative)
|
|
{
|
|
return from_digits(util::xbegin(container), util::xend(container), negative);
|
|
}
|
|
|
|
inline util::span<big_int::digit> big_int::digits()
|
|
{
|
|
return {digits_.get(), size()};
|
|
}
|
|
|
|
inline util::span<big_int::digit const> big_int::digits() const
|
|
{
|
|
return {digits_.get(), size()};
|
|
}
|
|
|
|
inline bool big_int::negative() const
|
|
{
|
|
return (size_ & sign_bit) == sign_bit;
|
|
}
|
|
|
|
template <std::integral T>
|
|
T big_int::shift_by_digit(T value)
|
|
{
|
|
if constexpr (sizeof(value) <= digit_size)
|
|
{
|
|
if (value < 0)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return value >> digit_bits;
|
|
}
|
|
}
|
|
|
|
template <std::integral T>
|
|
std::uint32_t big_int::size_for(T value)
|
|
{
|
|
std::uint32_t result_bits = 0;
|
|
|
|
if (value >= 0)
|
|
{
|
|
while (value > 0)
|
|
{
|
|
value = shift_by_digit(value);
|
|
result_bits += digit_bits;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result_bits = 1;
|
|
while ((value >> (digit_bits - 1)) != T(-1))
|
|
{
|
|
value = shift_by_digit(value);
|
|
result_bits += digit_bits;
|
|
}
|
|
}
|
|
|
|
return (result_bits + (digit_bits - 1)) / digit_bits;
|
|
}
|
|
|
|
}
|