232 lines
5.0 KiB
C++
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
|