From 08ea92908437518a0223368776aa920442d2f0d4 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Sat, 22 Nov 2025 19:36:26 +0100 Subject: [PATCH] New era --- .clang-format | 2 + .gitignore | 1 + main.cpp | 71 +++++++++++++++++--- mp/int.hpp | 97 ++++++++++++++++++++++++++++ mp/math.hpp | 172 +++++++++++++++++++++++++++++++++++++++++++++++++ mp/storage.hpp | 74 +++++++++++++++++++++ mp/utils.hpp | 150 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 558 insertions(+), 9 deletions(-) create mode 100644 mp/int.hpp create mode 100644 mp/math.hpp create mode 100644 mp/storage.hpp create mode 100644 mp/utils.hpp diff --git a/.clang-format b/.clang-format index 06546a8..0f3b668 100644 --- a/.clang-format +++ b/.clang-format @@ -4,3 +4,5 @@ PointerAlignment: Left IndentWidth: 4 # spaces per indent level TabWidth: 4 # width of a tab character UseTab: Never # options: Never, ForIndentation, Alwayss +BreakTemplateDeclarations: Yes +AllowShortFunctionsOnASingleLine: Inline diff --git a/.gitignore b/.gitignore index e524d79..24e741c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ +.vs/ build/ diff --git a/main.cpp b/main.cpp index 3bab47f..fc93991 100644 --- a/main.cpp +++ b/main.cpp @@ -1,31 +1,84 @@ #include -#include "mp.hpp" +//#include "mp.hpp" +#include "mp/int.hpp" +#include "mp/storage.hpp" +#include "mp/math.hpp" template static void PrintInt(const char* name, const T& val) { - std::cout << name << " = " << mp::ToHexString(val) << std::endl; + std::cout << name << " = " << mp::to_hex_string(val) << std::endl; } int main() { // mp::Int a{0xDEADBEEFDEADF154, 0x0123456789ABCDEF, 0x1111222233334444}; - mp::Int<4> a{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}; - mp::Int<5> b{0x55}; + mp::Int<32> a{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + mp::Int<1> b{0x55}; PrintInt("a", a); PrintInt("b", b); auto c = a + b; + //auto c = binary_opa, b); PrintInt("c", c); - a += mp::Int<1>{1}; - PrintInt("a", a); + //a += mp::Int<1>{1}; + //PrintInt("a", a); - a -= mp::Int<1>{1}; - PrintInt("a", a); + //a -= mp::Int<1>{1}; + //PrintInt("a", a); - std::cout << mp::Int<16>::LAST_ELEM_MASK << std::endl; + std::cout << mp::Int<17>::LAST_ELEM_MASK << std::endl; + std::cout << sizeof(mp::Int<1>) << std::endl; + std::cout << sizeof(mp::Int<2>) << std::endl; + std::cout << sizeof(mp::Int<3>) << std::endl; + std::cout << sizeof(mp::Int<4>) << std::endl; + std::cout << sizeof(mp::Int<5>) << std::endl; + std::cout << sizeof(mp::Int<6>) << std::endl; + std::cout << sizeof(mp::Int<7>) << std::endl; + std::cout << sizeof(mp::Int<8>) << std::endl; + std::cout << sizeof(mp::Int<9>) << std::endl; + std::cout << sizeof(mp::Int<10>) << std::endl; + std::cout << sizeof(mp::Int<20>) << std::endl; + std::cout << sizeof(mp::Int<30>) << std::endl; + std::cout << sizeof(mp::Int<40>) << std::endl; + std::cout << sizeof(mp::Int<50>) << std::endl; + std::cout << sizeof(mp::Int<60>) << std::endl; + std::cout << sizeof(mp::Int<70>) << std::endl; + std::cout << sizeof(mp::Int<80>) << std::endl; + std::cout << sizeof(mp::Int<90>) << std::endl; + std::cout << sizeof(mp::Int<100>) << std::endl; + std::cout << sizeof(mp::Int<110>) << std::endl; + std::cout << sizeof(mp::Int<120>) << std::endl; + std::cout << sizeof(mp::Int<130>) << std::endl; + std::cout << sizeof(mp::Int<140>) << std::endl; + std::cout << sizeof(mp::Int<150>) << std::endl; + std::cout << sizeof(mp::Int<160>) << std::endl; + + + + { + mp::Int<1024> acc{1}; + + try + { + while (true) + { + acc *= mp::Int<1>{10}; + PrintInt("acc", acc); + } + } + catch (const mp::OverflowErrorOf>& e) + { + std::cout << "overflow" << std::endl; + PrintInt("value", e.value()); + } + catch (const std::exception& e) + { + std::cout << "error: " << e.what() << std::endl; + } + } } \ No newline at end of file diff --git a/mp/int.hpp b/mp/int.hpp new file mode 100644 index 0000000..2f5eca8 --- /dev/null +++ b/mp/int.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include "utils.hpp" +#include "storage.hpp" + +namespace mp +{ + +template +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 = (MAX_BYTES + ELEMENT_BYTES - 1) / ELEMENT_BYTES; + constexpr static TElem LAST_ELEM_MASK = calculate_last_elem_mask(); + + BasicInt() = default; + + BasicInt(std::initializer_list init) + { + for (size_t i = 0; i < init.size(); ++i) + { + set(i, *(init.begin() + i)); + } + } + + 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]; + } + + //std::span data() { return m_data; } + + size_t size_elems() const { return m_data.size(); } + + private: + Container m_data; +}; + +template +using Int = BasicInt; + +} // namespace mp \ No newline at end of file diff --git a/mp/math.hpp b/mp/math.hpp new file mode 100644 index 0000000..99f5d9a --- /dev/null +++ b/mp/math.hpp @@ -0,0 +1,172 @@ +#pragma once + +#include "int.hpp" +#include "storage.hpp" +#include "utils.hpp" + +namespace mp +{ + +template +constexpr size_t ResultMaxSize = std::max(SizeA, SizeB); + +template + requires ElementTypesMatch +using OpResult = BasicInt>; + +template +class OverflowError : public std::runtime_error +{ + public: + OverflowError(T&& value) : std::runtime_error("Overflow"), m_value(std::move(value)) {} + + const T& value() const { return m_value; } + + private: + T m_value; +}; + +template +using OverflowErrorOf = OverflowError>; + +struct Addition +{ + template + requires ElementTypesMatch && ElementTypesMatch + static void invoke(const TLhs& lhs, const TRhs& rhs, TRes& res) + { + using ElementType = typename TRes::ElementType; + + res.zero(); + + ElementType carry = 0; + size_t end = std::max(lhs.size_elems(), rhs.size_elems()); + + for (size_t i = 0; i < end; ++i) + { + ElementType a = lhs.get(i); + ElementType b = rhs.get(i); + + ElementType c = carry + a; + carry = (c < a) ? 1 : 0; + c += b; + if (c < b) + carry = 1; + + res.set(i, c); + } + + res.set(end, carry); + } +}; + +struct Multiplication +{ + template + requires ElementTypesMatch && ElementTypesMatch + static void invoke(const TLhs& lhs, const TRhs& rhs, TRes& res) + { + using ElementType = typename TRes::ElementType; + using DoubleType = DoubleWidthType; + + res.zero(); + + bool overflow = false; + + const size_t n = lhs.size_elems(); + const size_t m = rhs.size_elems(); + + for (size_t i = 0; i < n; i++) + { + DoubleType carry = 0; + ElementType a = lhs[i]; + for (size_t j = 0; j < m; j++) + { + ElementType b = rhs[j]; + DoubleType t = static_cast(a) * b + res.get(i + j) + carry; + + overflow |= !res.try_set(i + j, static_cast(t)); + carry = t >> (sizeof(ElementType) * 8); + } + overflow |= !res.try_set(i + m, static_cast(carry)); + } + + if (overflow) + { + throw std::overflow_error("Multiplication overflow"); + } + }; +}; + +// Binary operation +template + requires ElementTypesMatch +OpResult binary_op(const TLhs& lhs, const TRhs& rhs) +{ + OpResult res{}; + + try + { + TOp::invoke(lhs, rhs, res); + } + catch (const std::overflow_error&) + { + throw OverflowErrorOf(std::move(res)); + } + + return res; +} + +// Compound assignment binary operation +template + requires ElementTypesMatch +TLhs& ca_binary_op(TLhs& lhs, const TRhs& rhs) +{ + TLhs res{}; + + try + { + TOp::invoke(lhs, rhs, res); + } + catch (const std::overflow_error&) + { + throw OverflowErrorOf(std::move(res)); + } + + lhs = std::move(res); + return lhs; +} + +// Addition operators + +template + requires ElementTypesMatch +OpResult operator+(const TLhs& lhs, const TRhs& rhs) +{ + return binary_op(lhs, rhs); +} + +template + requires ElementTypesMatch +TLhs& operator+=(TLhs& lhs, const TRhs& rhs) +{ + return ca_binary_op(lhs, rhs); +} + +// Multiplication operators + +template + requires ElementTypesMatch +OpResult operator*(const TLhs& lhs, const TRhs& rhs) +{ + return binary_op(lhs, rhs); +} + +template + requires ElementTypesMatch +TLhs& operator*=(TLhs& lhs, const TRhs& rhs) +{ + return ca_binary_op(lhs, rhs); +} + +} // namespace mp \ No newline at end of file diff --git a/mp/storage.hpp b/mp/storage.hpp new file mode 100644 index 0000000..697166c --- /dev/null +++ b/mp/storage.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" + +namespace mp +{ + +template +class ArrayContainer +{ + public: + ArrayContainer() = default; + + TElem& operator[](size_t idx) { return m_data[idx]; } + const TElem& operator[](size_t idx) const { return m_data[idx]; } + + size_t size() const { return m_size; } + void clear() { m_size = 0; } + + void resize(size_t new_size) + { + if (new_size > MaxSize) + throw std::out_of_range("New size exceeds maximum number of elements"); + + if (new_size > m_size) + std::fill(m_data.begin() + m_size, m_data.begin() + new_size, TElem{0}); + + m_size = new_size; + } + + private: + size_t m_size = 0; + std::array m_data; +}; + +template +class VectorContainer +{ + public: + VectorContainer() = default; + + TElem& operator[](size_t idx) { return m_data[idx]; } + const TElem& operator[](size_t idx) const { return m_data[idx]; } + + size_t size() const { return m_data.size(); } + void clear() { m_data.clear(); } + + void resize(size_t new_size) + { + if (new_size > MaxSize) + throw std::out_of_range("New size exceeds maximum number of elements"); + + m_data.resize(new_size, TElem{0}); + } + + private: + std::vector m_data; +}; + +// threshold for self-contained storage +constexpr size_t MAX_ARRAY_BYTES = 128; + +template +using Container = + std::conditional_t<(sizeof(TElem) * MaxSize > MAX_ARRAY_BYTES), VectorContainer, ArrayContainer>; + +} // namespace mp \ No newline at end of file diff --git a/mp/utils.hpp b/mp/utils.hpp new file mode 100644 index 0000000..868be88 --- /dev/null +++ b/mp/utils.hpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include + +namespace mp +{ + +//======================== DoubleWidthType =======================// + +template +struct DoubleWidth; + +template <> +struct DoubleWidth +{ + using type = uint16_t; +}; + +template <> +struct DoubleWidth +{ + using type = uint32_t; +}; + +template <> +struct DoubleWidth +{ + using type = uint64_t; +}; + +#ifdef __SIZEOF_INT128__ +template <> +struct DoubleWidth +{ + using type = __uint128_t; +}; +#endif + +template +using DoubleWidthType = typename DoubleWidth::type; + +//======================== HalfWidthType =======================// + +template +struct HalfWidth; + +template <> +struct HalfWidth +{ + using type = uint8_t; +}; + +template <> +struct HalfWidth +{ + using type = uint16_t; +}; + +template <> +struct HalfWidth +{ + using type = uint32_t; +}; + +template +using HalfWidthType = typename HalfWidth::type; + +//======================== Concepts =======================// + +template +concept ElementSuitable = std::unsigned_integral; + +template +concept AnyInt = requires(T t, TElem a, size_t i) { + { t[i] } -> std::convertible_to; + { t.set(i, a) }; + { t.try_set(i, a) } -> std::convertible_to; + { t.get(i) } -> std::convertible_to; + { t.zero() }; + { t.size_elems() } -> std::convertible_to; + { T::MAX_BYTES } -> std::convertible_to; + { T::MAX_ELEMS } -> std::convertible_to; + { T::ELEMENT_BYTES } -> std::convertible_to; + { T::LAST_ELEM_MASK } -> std::convertible_to; +}; + +//======================== Utils =======================// + +#ifdef __SIZEOF_INT128__ +using LongestElementSuitableType = uint64_t; +#else +using LongestElementSuitableType = HalfWidthType; +#endif + +template +constexpr bool ElementTypesMatch = std::is_same_v; + +template +constexpr TElem calculate_last_elem_mask() +{ + size_t last_elem_bytes = MaxBytes % sizeof(TElem); + TElem mask = 0; + + if (last_elem_bytes == 0) + { + return ~mask; + } + + for (size_t i = 0; i < last_elem_bytes; ++i) + { + mask <<= 8; + mask |= 0xFF; + } + return mask; +} + +inline char hex_digit(uint8_t bits) +{ + return bits > 9 ? 'a' + (bits - 10) : '0' + bits; +} + +template +std::string to_hex_string(const T& number) +{ + constexpr size_t ELEMENT_DIGITS = T::ELEMENT_BYTES * 2; + + std::string str(number.size_elems() * ELEMENT_DIGITS, '-'); + + for (size_t elem = 0; elem < number.size_elems(); ++elem) + { + auto v = number[elem]; + for (size_t digit = 0; digit < ELEMENT_DIGITS; ++digit) + { + str[str.size() - 1 - (elem * ELEMENT_DIGITS) - digit] = hex_digit((v >> (digit * 4)) & 0xF); + } + } + + auto first = str.find_first_not_of('0'); + if (first != std::string::npos) + { + return "0x" + str.substr(first); + } + + return "0x0"; + +} + +} \ No newline at end of file