cpp_mp/mp/int.hpp
2025-12-04 10:03:30 +01:00

232 lines
5.0 KiB
C++

#pragma once
#include <limits>
#include "utils.hpp"
#include "storage.hpp"
namespace mp
{
template <AnyMpInt T>
inline void parse_string(const char* str, T& number)
{
number.zero();
bool negative = false;
if (*str == '-')
{
negative = true;
++str;
}
for (; *str; ++str)
{
if (*str < '0' || *str > '9')
{
throw std::invalid_argument("Invalid character in input string");
}
number = number * 10U + static_cast<typename T::ElementType>(*str - '0');
}
number.set_negative(negative);
}
template <ElementSuitable TElem, size_t MaxBytes>
class BasicInt
{
public:
using ElementType = TElem;
constexpr static size_t MAX_BYTES = MaxBytes;
constexpr static size_t ELEMENT_BYTES = sizeof(TElem);
constexpr static size_t MAX_ELEMS = calculate_max_elems(MAX_BYTES, ELEMENT_BYTES);
constexpr static TElem LAST_ELEM_MASK = calculate_last_elem_mask<TElem, MAX_BYTES>();
BasicInt() = default;
BasicInt(std::initializer_list<ElementType> init)
{
for (size_t i = 0; i < init.size(); ++i)
{
set(i, *(init.begin() + i));
}
}
BasicInt(const BasicInt& other) = default;
BasicInt(BasicInt&& other) noexcept = default;
BasicInt& operator=(const BasicInt& other) = default;
BasicInt& operator=(BasicInt&& other) noexcept = default;
template <AnyMpInt T>
BasicInt(const T& other)
{
*this = other;
}
template <AnyRegularInt T>
BasicInt(T value)
{
*this = value;
}
BasicInt(const char* str)
{
*this = str;
}
template <AnyMpInt T>
requires ElementTypesMatch<T, BasicInt>
BasicInt& operator=(const T& other)
{
size_t other_size = other.size_elems();
for (size_t i = 0; i < other_size; ++i)
{
set(i, other.get(i));
}
if (size_elems() > other_size)
resize(other_size);
set_negative(other.negative());
return *this;
}
template <AnyRegularInt T>
BasicInt& operator=(T value)
{
using UnsignedType = std::make_unsigned_t<T>;
UnsignedType uvalue;
if (std::is_signed_v<T> && value < 0)
{
uvalue = static_cast<UnsignedType>(-value);
set_negative(true);
}
else
{
uvalue = static_cast<UnsignedType>(value);
set_negative(false);
}
zero();
if constexpr (sizeof(UnsignedType) <= sizeof(TElem))
{
set(0, static_cast<TElem>(uvalue));
}
else
{
constexpr UnsignedType ELEM_MASK = static_cast<UnsignedType>(std::numeric_limits<TElem>::max());
size_t idx = 0;
while (uvalue != 0)
{
set(idx, static_cast<TElem>(uvalue & ELEM_MASK));
uvalue >>= (sizeof(TElem) * 8);
idx++;
}
}
return *this;
}
BasicInt& operator=(const char* str)
{
parse_string(str, *this);
return *this;
}
BasicInt operator-() const
{
BasicInt res = *this;
res.set_negative(!res.negative());
return res;
}
TElem& operator[](size_t index) { return m_data[index]; }
const TElem& operator[](size_t index) const { return m_data[index]; }
void resize(size_t new_size)
{
m_data.resize(new_size);
}
void zero() { m_data.clear(); }
bool try_set(size_t idx, TElem value)
{
if (idx >= m_data.size())
{
if (value == 0)
{
return true;
}
if (idx >= MAX_ELEMS)
{
return false;
}
m_data.resize(idx + 1);
}
m_data[idx] = value;
if (idx == MAX_ELEMS - 1 && value > LAST_ELEM_MASK)
{
m_data[idx] &= LAST_ELEM_MASK;
return false;
}
return true;
}
void set(size_t idx, TElem value)
{
if (!try_set(idx, value))
{
throw std::out_of_range("Value exceeds maximum size");
}
}
TElem get(size_t idx) const
{
if (idx >= m_data.size())
{
return TElem{0};
}
return m_data[idx];
}
void fix_leading_zeros()
{
size_t new_size = m_data.size();
while (new_size > 0 && m_data[new_size - 1] == TElem{0})
{
new_size--;
}
m_data.resize(new_size);
}
//std::span<TElem> data() { return m_data; }
size_t size_elems() const { return m_data.size(); }
bool negative() const { return m_negative; }
void set_negative(bool neg) { m_negative = neg; }
private:
Container<TElem, MAX_ELEMS> m_data;
bool m_negative = false;
};
template<size_t MaxBytes>
using Int = BasicInt<LongestElementSuitableType, MaxBytes>;
constexpr size_t UNLIMITED = std::numeric_limits<size_t>::max();
using UnlimitedInt = BasicInt<LongestElementSuitableType, UNLIMITED>;
} // namespace mp