32-bit compilation fixes:

* Use uint64_t instead of size_t as hash return value

 * Expect alignof(uint64_t) <= 8 instead of == 8
This commit is contained in:
Nikita Lisitsa 2025-01-25 20:35:37 +03:00
parent 7c15c1bb0d
commit 2c3565df61
19 changed files with 545 additions and 59 deletions

View file

@ -9,7 +9,7 @@ namespace psemek::ecs::detail
template <bool With> template <bool With>
struct ordered_component_hasher struct ordered_component_hasher
{ {
std::size_t result = 0xb6113227befa663bull; std::uint64_t result = 0xb6113227befa663bull;
void operator()(util::uuid const & uuid) void operator()(util::uuid const & uuid)
{ {
@ -22,7 +22,7 @@ namespace psemek::ecs::detail
}; };
template <bool With, typename UUIDs> template <bool With, typename UUIDs>
std::size_t ordered_component_hash(UUIDs const & uuids) std::uint64_t ordered_component_hash(UUIDs const & uuids)
{ {
ordered_component_hasher<With> hasher; ordered_component_hasher<With> hasher;
for (auto const & uuid : uuids) for (auto const & uuid : uuids)
@ -31,7 +31,7 @@ namespace psemek::ecs::detail
} }
template <typename WithUUIDs, typename WithoutUUIDs> template <typename WithUUIDs, typename WithoutUUIDs>
std::size_t ordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids) std::uint64_t ordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
{ {
return ordered_component_hash<true>(with_uuids) ^ ordered_component_hash<false>(without_uuids); return ordered_component_hash<true>(with_uuids) ^ ordered_component_hash<false>(without_uuids);
} }

View file

@ -9,12 +9,12 @@ namespace psemek::ecs::detail
struct table_hashset_hash struct table_hashset_hash
{ {
std::size_t operator()(util::span<util::uuid const> const & uuids) const std::uint64_t operator()(util::span<util::uuid const> const & uuids) const
{ {
return unordered_component_hash<true>(uuids); return unordered_component_hash<true>(uuids);
} }
std::size_t operator()(std::unique_ptr<table> const & table) const std::uint64_t operator()(std::unique_ptr<table> const & table) const
{ {
return table->hash(); return table->hash();
} }

View file

@ -9,7 +9,7 @@ namespace psemek::ecs::detail
template <bool With> template <bool With>
struct unordered_component_hasher struct unordered_component_hasher
{ {
std::size_t result = 0xcd5694d2b3f3443eull; std::uint64_t result = 0xcd5694d2b3f3443eull;
void operator()(util::uuid const & uuid) void operator()(util::uuid const & uuid)
{ {
@ -21,7 +21,7 @@ namespace psemek::ecs::detail
}; };
template <bool With, typename UUIDs> template <bool With, typename UUIDs>
std::size_t unordered_component_hash(UUIDs const & uuids) std::uint64_t unordered_component_hash(UUIDs const & uuids)
{ {
unordered_component_hasher<With> hasher; unordered_component_hasher<With> hasher;
for (auto const & uuid : uuids) for (auto const & uuid : uuids)
@ -30,7 +30,7 @@ namespace psemek::ecs::detail
} }
template <typename WithUUIDs, typename WithoutUUIDs> template <typename WithUUIDs, typename WithoutUUIDs>
std::size_t unordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids) std::uint64_t unordered_component_hash(WithUUIDs const & with_uuids, WithoutUUIDs const & without_uuids)
{ {
return unordered_component_hash<true>(with_uuids) ^ unordered_component_hash<false>(without_uuids); return unordered_component_hash<true>(with_uuids) ^ unordered_component_hash<false>(without_uuids);
} }

View file

@ -40,9 +40,9 @@ namespace std
template <> template <>
struct hash<::psemek::ecs::handle> struct hash<::psemek::ecs::handle>
{ {
size_t operator()(::psemek::ecs::handle const & h) const noexcept uint64_t operator()(::psemek::ecs::handle const & h) const noexcept
{ {
return (static_cast<size_t>(h.id) << 32) | h.epoch; return (static_cast<uint64_t>(h.id) << 32) | h.epoch;
} }
}; };

View file

@ -7,8 +7,7 @@
#include <psemek/math/matrix.hpp> #include <psemek/math/matrix.hpp>
#include <psemek/math/interval.hpp> #include <psemek/math/interval.hpp>
#include <psemek/math/quaternion.hpp> #include <psemek/math/quaternion.hpp>
#include <psemek/util/hash_table.hpp>
#include <boost/container/flat_map.hpp>
#include <string_view> #include <string_view>
@ -121,8 +120,8 @@ namespace psemek::gfx
private: private:
GLuint program_ = 0; GLuint program_ = 0;
mutable boost::container::flat_map<std::string, GLint, std::less<>> uniforms_; mutable util::hash_map<std::string, GLint, util::any_hash, std::equal_to<>> uniforms_;
mutable boost::container::flat_map<std::string, GLuint, std::less<>> uniform_blocks_; mutable util::hash_map<std::string, GLuint, util::any_hash, std::equal_to<>> uniform_blocks_;
program(std::nullptr_t) program(std::nullptr_t)
{} {}

View file

@ -222,10 +222,10 @@ namespace std
template <typename T, std::size_t N> template <typename T, std::size_t N>
struct hash<::psemek::math::point<T, N>> struct hash<::psemek::math::point<T, N>>
{ {
std::size_t operator()(::psemek::math::point<T, N> const & v) const noexcept std::uint64_t operator()(::psemek::math::point<T, N> const & v) const noexcept
{ {
std::hash<T> h; hash<T> h;
std::size_t r = 0; std::uint64_t r = 0;
for (std::size_t i = 0; i < N; ++i) for (std::size_t i = 0; i < N; ++i)
::psemek::util::hash_combine(r, h(v[i])); ::psemek::util::hash_combine(r, h(v[i]));
return r; return r;

View file

@ -139,9 +139,9 @@ namespace std
template <typename Point, std::size_t K> template <typename Point, std::size_t K>
struct hash<::psemek::math::simplex<Point, K>> struct hash<::psemek::math::simplex<Point, K>>
{ {
std::size_t operator()(::psemek::math::simplex<Point, K> const & simplex) const std::uint64_t operator()(::psemek::math::simplex<Point, K> const & simplex) const
{ {
std::size_t result = 0; std::uint64_t result = 0;
std::hash<Point> h; std::hash<Point> h;
for (auto const & p : simplex.points) for (auto const & p : simplex.points)
::psemek::util::hash_combine(result, h(p)); ::psemek::util::hash_combine(result, h(p));

View file

@ -472,10 +472,10 @@ namespace std
template <typename T, std::size_t N> template <typename T, std::size_t N>
struct hash<::psemek::math::vector<T, N>> struct hash<::psemek::math::vector<T, N>>
{ {
std::size_t operator()(::psemek::math::vector<T, N> const & v) const noexcept std::uint64_t operator()(::psemek::math::vector<T, N> const & v) const noexcept
{ {
std::hash<T> h; std::hash<T> h;
std::size_t r = 0; std::uint64_t r = 0;
for (std::size_t i = 0; i < N; ++i) for (std::size_t i = 0; i < N; ++i)
::psemek::util::hash_combine(r, h(v[i])); ::psemek::util::hash_combine(r, h(v[i]));
return r; return r;

View file

@ -19,14 +19,14 @@ namespace psemek::sir
static_assert(alignof(std::uint8_t) == 1); static_assert(alignof(std::uint8_t) == 1);
static_assert(alignof(std::uint16_t) == 2); static_assert(alignof(std::uint16_t) == 2);
static_assert(alignof(std::uint32_t) == 4); static_assert(alignof(std::uint32_t) == 4);
static_assert(alignof(std::uint64_t) == 8); static_assert(alignof(std::uint64_t) <= 8);
static_assert(alignof(std::int8_t) == 1); static_assert(alignof(std::int8_t) == 1);
static_assert(alignof(std::int16_t) == 2); static_assert(alignof(std::int16_t) == 2);
static_assert(alignof(std::int32_t) == 4); static_assert(alignof(std::int32_t) == 4);
static_assert(alignof(std::int64_t) == 8); static_assert(alignof(std::int64_t) <= 8);
static_assert(sizeof(float) == 4); static_assert(sizeof(float) == 4);
static_assert(alignof(float) == 4); static_assert(alignof(float) == 4);
static_assert(sizeof(double) == 8); static_assert(sizeof(double) == 8);
static_assert(alignof(double) == 8); static_assert(alignof(double) <= 8);
} }

View file

@ -0,0 +1,197 @@
#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;
}
}

View file

@ -97,9 +97,9 @@ namespace std
template <> template <>
struct hash<::psemek::util::dynamic_bitset> struct hash<::psemek::util::dynamic_bitset>
{ {
std::size_t operator()(::psemek::util::dynamic_bitset const & b) const std::uint64_t operator()(::psemek::util::dynamic_bitset const & b) const
{ {
std::size_t result = 0x23d2ef655094f9aeull; std::uint64_t result = static_cast<std::size_t>(0x23d2ef655094f9aeull);
for (auto value : b.storage()) for (auto value : b.storage())
::psemek::util::hash_combine(result, value); ::psemek::util::hash_combine(result, value);
return result; return result;

View file

@ -962,7 +962,7 @@ namespace std
template <> template <>
struct hash<::psemek::util::ecs::species_handle> struct hash<::psemek::util::ecs::species_handle>
{ {
std::size_t operator()(::psemek::util::ecs::species_handle h) const std::uint64_t operator()(::psemek::util::ecs::species_handle h) const
{ {
return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value); return std::hash<::psemek::util::ecs_detail::species_handle>()(h.value);
} }
@ -971,7 +971,7 @@ namespace std
template <> template <>
struct hash<::psemek::util::ecs::entity_handle> struct hash<::psemek::util::ecs::entity_handle>
{ {
std::size_t operator()(::psemek::util::ecs::entity_handle h) const std::uint64_t operator()(::psemek::util::ecs::entity_handle h) const
{ {
return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value); return std::hash<::psemek::util::ecs_detail::entity_handle>()(h.value);
} }

View file

@ -2,22 +2,23 @@
#include <functional> #include <functional>
#include <tuple> #include <tuple>
#include <cstdint>
namespace psemek::util namespace psemek::util
{ {
constexpr void hash_combine(std::size_t & seed, std::size_t value) constexpr void hash_combine(std::uint64_t & seed, std::uint64_t value)
{ {
constexpr std::size_t k = 0x9ddfea08eb382d69ULL; constexpr std::uint64_t k = 0x9ddfea08eb382d69ULL;
std::size_t a = (value ^ seed) * k; std::uint64_t a = (value ^ seed) * k;
a ^= (a >> 47); a ^= (a >> 47);
std::size_t b = (seed ^ a) * k; std::uint64_t b = (seed ^ a) * k;
b ^= (b >> 47); b ^= (b >> 47);
seed = b * k; seed = b * k;
} }
template <typename Iterator, typename Hash = std::hash<std::decay_t<decltype(*std::declval<Iterator>())>>> template <typename Iterator, typename Hash = std::hash<std::decay_t<decltype(*std::declval<Iterator>())>>>
constexpr void hash_sequence(std::size_t & seed, Iterator begin, Iterator end, Hash hash = Hash{}) constexpr void hash_sequence(std::uint64_t & seed, Iterator begin, Iterator end, Hash hash = Hash{})
{ {
for (; begin != end; ++begin) for (; begin != end; ++begin)
{ {
@ -30,17 +31,17 @@ namespace psemek::util
struct is_transparent{}; struct is_transparent{};
template <typename T> template <typename T>
std::size_t operator() (T const & x) const std::uint64_t operator() (T const & x) const
{ {
return std::hash<T>{}(x); return std::hash<T>{}(x);
} }
}; };
template <typename ... T> template <typename ... T>
constexpr std::size_t hash_all(T const & ... x) constexpr std::uint64_t hash_all(T const & ... x)
{ {
any_hash hash; any_hash hash;
std::size_t seed = 0; std::uint64_t seed = 0;
(hash_combine(seed, hash(x)), ...); (hash_combine(seed, hash(x)), ...);
return seed; return seed;
} }
@ -54,9 +55,9 @@ namespace std
struct hash<std::pair<T, H>> struct hash<std::pair<T, H>>
: std::pair<std::hash<T>, std::hash<H>> : std::pair<std::hash<T>, std::hash<H>>
{ {
std::size_t operator()(std::pair<T, H> const & x) const std::uint64_t operator()(std::pair<T, H> const & x) const
{ {
std::size_t seed = 0; std::uint64_t seed = 0;
::psemek::util::hash_combine(seed, this->first(x.first)); ::psemek::util::hash_combine(seed, this->first(x.first));
::psemek::util::hash_combine(seed, this->second(x.second)); ::psemek::util::hash_combine(seed, this->second(x.second));
return seed; return seed;
@ -67,16 +68,16 @@ namespace std
struct hash<std::tuple<Ts...>> struct hash<std::tuple<Ts...>>
: std::tuple<std::hash<Ts>...> : std::tuple<std::hash<Ts>...>
{ {
std::size_t operator()(std::tuple<Ts...> const & t) const std::uint64_t operator()(std::tuple<Ts...> const & t) const
{ {
return hash_impl(t, std::make_index_sequence<sizeof...(Ts)>{}); return hash_impl(t, std::make_index_sequence<sizeof...(Ts)>{});
} }
private: private:
template <std::size_t ... Is> template <std::size_t ... Is>
std::size_t hash_impl(std::tuple<Ts...> const & t, std::index_sequence<Is...>) const std::uint64_t hash_impl(std::tuple<Ts...> const & t, std::index_sequence<Is...>) const
{ {
std::size_t result = 0; std::uint64_t result = 0;
(::psemek::util::hash_combine(result, std::get<Is>(*this)(std::get<Is>(t))), ...); (::psemek::util::hash_combine(result, std::get<Is>(*this)(std::get<Is>(t))), ...);
return result; return result;
} }

View file

@ -13,14 +13,14 @@ namespace psemek::util
namespace detail namespace detail
{ {
constexpr std::size_t stored_value_mask = 1ull << 63; constexpr std::uint64_t stored_value_mask = 1ull << 63;
constexpr std::size_t tombstone_mask = 1ull << 62; constexpr std::uint64_t tombstone_mask = 1ull << 62;
constexpr std::size_t hash_value_mask = ~(stored_value_mask | tombstone_mask); constexpr std::uint64_t hash_value_mask = ~(stored_value_mask | tombstone_mask);
template <typename T> template <typename T>
struct hash_table_entry struct hash_table_entry
{ {
std::size_t hash = 0; std::uint64_t hash = 0;
alignas(T) char storage[sizeof(T)] = {0}; alignas(T) char storage[sizeof(T)] = {0};
bool has_value() const bool has_value() const
@ -44,13 +44,13 @@ namespace psemek::util
} }
template <typename H> template <typename H>
void set_value(H && value, std::size_t hash) void set_value(H && value, std::uint64_t hash)
{ {
new (storage_ptr()) T{std::forward<H>(value)}; new (storage_ptr()) T{std::forward<H>(value)};
this->hash = (hash & hash_value_mask) | stored_value_mask; this->hash = (hash & hash_value_mask) | stored_value_mask;
} }
bool hash_equal(std::size_t hash) const bool hash_equal(std::uint64_t hash) const
{ {
return (hash & hash_value_mask) == (this->hash & hash_value_mask); return (hash & hash_value_mask) == (this->hash & hash_value_mask);
} }
@ -211,7 +211,7 @@ namespace psemek::util
std::pair<hash_table_iterator<T>, bool> insert(H && value) std::pair<hash_table_iterator<T>, bool> insert(H && value)
{ {
ensure_capacity_for(size_ + 1); ensure_capacity_for(size_ + 1);
std::size_t hash = this->hash()(this->key_projector()(value)); std::uint64_t hash = this->hash()(this->key_projector()(value));
return insert_impl(std::forward<H>(value), hash); return insert_impl(std::forward<H>(value), hash);
} }
@ -220,7 +220,7 @@ namespace psemek::util
{ {
if (size_ == 0) if (size_ == 0)
return end(); return end();
std::size_t hash = this->hash()(key); std::uint64_t hash = this->hash()(key);
return find_impl(key, hash); return find_impl(key, hash);
} }
@ -320,13 +320,13 @@ namespace psemek::util
reallocate(capacity()); reallocate(capacity());
} }
std::size_t probe_index(std::size_t hash, std::size_t i) const std::size_t probe_index(std::uint64_t hash, std::size_t i) const
{ {
return (hash + (i * (i + 1)) / 2) % storage_.capacity; return (static_cast<std::size_t>(hash) + (i * (i + 1)) / 2) % storage_.capacity;
} }
template <typename H> template <typename H>
std::pair<hash_table_iterator<T>, bool> insert_impl(H && value, std::size_t hash) std::pair<hash_table_iterator<T>, bool> insert_impl(H && value, std::uint64_t hash)
{ {
std::size_t i = 0; std::size_t i = 0;
while (true) while (true)
@ -349,7 +349,7 @@ namespace psemek::util
} }
template <typename Key> template <typename Key>
hash_table_iterator<T> find_impl(Key const & key, std::size_t hash) const hash_table_iterator<T> find_impl(Key const & key, std::uint64_t hash) const
{ {
std::size_t i = 0; std::size_t i = 0;
while (true) while (true)

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <functional> #include <functional>
#include <cstdint>
namespace psemek::util namespace psemek::util
{ {
@ -87,7 +88,7 @@ namespace std
template <> template <>
struct hash<::psemek::util::hstring> struct hash<::psemek::util::hstring>
{ {
std::size_t operator()(::psemek::util::hstring const & str) const std::uint64_t operator()(::psemek::util::hstring const & str) const
{ {
return std::hash<std::string_view>()(str.view()); return std::hash<std::string_view>()(str.view());
} }

View file

@ -2,7 +2,6 @@
#include <psemek/util/hash.hpp> #include <psemek/util/hash.hpp>
#include <array>
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
#include <iostream> #include <iostream>
@ -29,7 +28,7 @@ namespace psemek::util
}; };
static_assert(sizeof(uuid) == 16); static_assert(sizeof(uuid) == 16);
static_assert(alignof(uuid) == 8); static_assert(alignof(uuid) <= 8);
constexpr bool is_rfc_4122(uuid const & uuid) constexpr bool is_rfc_4122(uuid const & uuid)
{ {
@ -72,9 +71,9 @@ namespace std
template <> template <>
struct hash<::psemek::util::uuid> struct hash<::psemek::util::uuid>
{ {
std::size_t operator()(::psemek::util::uuid const & uuid) const noexcept std::uint64_t operator()(::psemek::util::uuid const & uuid) const noexcept
{ {
std::size_t result = uuid[0]; std::uint64_t result = uuid[0];
::psemek::util::hash_combine(result, uuid[1]); ::psemek::util::hash_combine(result, uuid[1]);
return result; return result;
} }

View file

@ -0,0 +1,110 @@
#include <psemek/util/big_int.hpp>
#include <algorithm>
namespace psemek::util
{
namespace
{
struct adder
{
std::uint32_t sum = 0;
std::uint32_t carry = 0;
void add(std::uint32_t x)
{
auto result = static_cast<std::uint64_t>(sum) + x;
sum = result & std::uint32_t(-1);
carry += result >> 32;
}
};
}
void big_int::canonicalize()
{
auto size = this->size();
auto negative = this->negative();
while (size > 0 && digits_[size - 1] == 0)
--size;
set_size(size);
set_negative(size > 0 && negative);
}
void big_int::ensure_capacity(std::uint32_t new_capacity)
{
if (new_capacity == 0)
{
digits_ = nullptr;
size_ = 0;
capacity_ = 0;
}
else if (new_capacity > capacity_)
{
new_capacity = std::max({static_cast<std::uint32_t>(1.6 * size()), static_cast<std::uint32_t>(16), new_capacity});
auto new_digits = std::make_unique<digit[]>(new_capacity);
std::size_t i = 0;
for (; i < size(); ++i)
new_digits[i] = digits_[i];
for (; i < new_capacity; ++i)
new_digits[i] = 0;
digits_ = std::move(new_digits);
capacity_ = new_capacity;
}
}
void big_int::add_positive(span<digit const> digits)
{
digit carry = 0;
std::size_t i = 0;
std::size_t min_size = std::min<std::size_t>(size(), digits.size());
for (; i < min_size; ++i)
{
adder adder{digits_[i]};
adder.add(digits[i]);
adder.add(carry);
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
}
if (i < digits.size())
{
ensure_capacity(digits.size());
for (; i < digits.size(); ++i)
{
adder adder{digits[i]};
adder.add(carry);
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
}
set_size(digits.size());
}
if (i < size())
{
for (; i < size() && carry != 0; ++i)
{
adder adder{digits_[i]};
adder.add(carry);
std::tie(digits_[i], carry) = {adder.sum, adder.carry};
}
}
if (carry != 0)
{
ensure_capacity(size() + 1);
digits_[size()] = carry;
set_size(size() + 1);
}
canonicalize();
}
}

179
libs/util/tests/big_int.cpp Normal file
View file

@ -0,0 +1,179 @@
#include <psemek/test/test.hpp>
#include <psemek/util/big_int.hpp>
#include <psemek/random/generator.hpp>
#include <psemek/random/uniform.hpp>
using namespace psemek;
using namespace psemek::util;
namespace
{
void check_digits(big_int const & bi, std::vector<big_int::digit> const & digits_expected)
{
span<big_int::digit const> digits_actual = bi.digits();
expect_equal(digits_actual.size(), digits_expected.size());
for (std::size_t i = 0; i < digits_actual.size(); ++i)
expect_equal(digits_actual[i], digits_expected[i]);
}
}
test_case(util_big__int_init_small)
{
{
big_int x;
expect(!x.negative());
check_digits(x, {});
}
{
big_int x(1);
expect(!x.negative());
check_digits(x, {1});
}
{
big_int x(173);
expect(!x.negative());
check_digits(x, {173});
}
{
big_int x(1u << 31);
expect(!x.negative());
check_digits(x, {1u << 31});
}
{
big_int x(1ull << 32);
expect(!x.negative());
check_digits(x, {0, 1});
}
{
big_int x(1ull << 63);
expect(!x.negative());
check_digits(x, {0, 1u << 31});
}
{
big_int x(static_cast<std::uint64_t>(-1));
expect(!x.negative());
check_digits(x, {-1, -1});
}
{
big_int x(-1);
expect(x.negative());
check_digits(x, {1});
}
{
big_int x(-279);
expect(x.negative());
check_digits(x, {279});
}
{
big_int x((-1) << 31);
expect(x.negative());
check_digits(x, {1u << 31});
}
{
big_int x((-1ll) << 32);
expect(x.negative());
check_digits(x, {0, 1u});
}
{
big_int x((-1ll) << 63);
expect(x.negative());
check_digits(x, {0, 1u << 31});
}
{
big_int x(((-1ll) << 62) + ((-1ll) << 15));
expect(x.negative());
check_digits(x, {1u << 15, 1u << 30});
}
}
test_case(util_big__int_init_from__digits)
{
random::generator rng;
{
big_int x = big_int::from_digits(std::vector<int>{});
expect(!x.negative());
check_digits(x, {});
}
{
big_int x = big_int::from_digits(std::vector{5724});
expect(!x.negative());
check_digits(x, {5724});
}
{
big_int x = big_int::from_digits(std::vector{1234, 5678});
expect(!x.negative());
check_digits(x, {1234, 5678});
}
{
big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0});
expect(!x.negative());
check_digits(x, {1234, 5678});
}
{
std::vector<big_int::digit> digits(64);
for (auto & digit : digits)
digit = random::uniform<big_int::digit>(rng);
if (digits.back() == 0)
digits.back() = 1;
big_int x = big_int::from_digits(digits);
expect(!x.negative());
check_digits(x, digits);
}
{
big_int x = big_int::from_digits(std::vector<int>{}, true);
expect(!x.negative());
check_digits(x, {});
}
{
big_int x = big_int::from_digits(std::vector{5724}, true);
expect(x.negative());
check_digits(x, {5724});
}
{
big_int x = big_int::from_digits(std::vector{1234, 5678}, true);
expect(x.negative());
check_digits(x, {1234, 5678});
}
{
big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0}, true);
expect(x.negative());
check_digits(x, {1234, 5678});
}
{
std::vector<big_int::digit> digits(64);
for (auto & digit : digits)
digit = random::uniform<big_int::digit>(rng);
if (digits.back() == 0)
digits.back() = 1;
big_int x = big_int::from_digits(digits, true);
expect(x.negative());
check_digits(x, digits);
}
}

View file

@ -166,7 +166,7 @@ std::optional<std::uint64_t> parse_seed(std::string const & str, int base)
{ {
try try
{ {
std::uint64_t unused; std::size_t unused;
return std::stoull(str, &unused, base); return std::stoull(str, &unused, base);
} }
catch (...) catch (...)