New era
This commit is contained in:
parent
ac10af30da
commit
08ea929084
@ -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
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.vscode/
|
||||
.vs/
|
||||
build/
|
||||
|
||||
71
main.cpp
71
main.cpp
@ -1,31 +1,84 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "mp.hpp"
|
||||
//#include "mp.hpp"
|
||||
#include "mp/int.hpp"
|
||||
#include "mp/storage.hpp"
|
||||
#include "mp/math.hpp"
|
||||
|
||||
template <class T>
|
||||
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<decltype(acc), mp::Int<1>>& e)
|
||||
{
|
||||
std::cout << "overflow" << std::endl;
|
||||
PrintInt("value", e.value());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
mp/int.hpp
Normal file
97
mp/int.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "storage.hpp"
|
||||
|
||||
namespace mp
|
||||
{
|
||||
|
||||
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 = (MAX_BYTES + ELEMENT_BYTES - 1) / 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));
|
||||
}
|
||||
}
|
||||
|
||||
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<TElem> data() { return m_data; }
|
||||
|
||||
size_t size_elems() const { return m_data.size(); }
|
||||
|
||||
private:
|
||||
Container<TElem, MAX_ELEMS> m_data;
|
||||
};
|
||||
|
||||
template<size_t MaxBytes>
|
||||
using Int = BasicInt<LongestElementSuitableType, MaxBytes>;
|
||||
|
||||
} // namespace mp
|
||||
172
mp/math.hpp
Normal file
172
mp/math.hpp
Normal file
@ -0,0 +1,172 @@
|
||||
#pragma once
|
||||
|
||||
#include "int.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace mp
|
||||
{
|
||||
|
||||
template <size_t SizeA, size_t SizeB>
|
||||
constexpr size_t ResultMaxSize = std::max(SizeA, SizeB);
|
||||
|
||||
template <AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
using OpResult = BasicInt<typename TLhs::ElementType, ResultMaxSize<TLhs::MAX_BYTES, TRhs::MAX_BYTES>>;
|
||||
|
||||
template <AnyInt T>
|
||||
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 <AnyInt TLhs, AnyInt TRhs>
|
||||
using OverflowErrorOf = OverflowError<OpResult<TLhs, TRhs>>;
|
||||
|
||||
struct Addition
|
||||
{
|
||||
template <AnyInt TLhs, AnyInt TRhs, AnyInt TRes>
|
||||
requires ElementTypesMatch<TLhs, TRes> && ElementTypesMatch<TRhs, TRes>
|
||||
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 <AnyInt TLhs, AnyInt TRhs, AnyInt TRes>
|
||||
requires ElementTypesMatch<TLhs, TRes> && ElementTypesMatch<TRhs, TRes>
|
||||
static void invoke(const TLhs& lhs, const TRhs& rhs, TRes& res)
|
||||
{
|
||||
using ElementType = typename TRes::ElementType;
|
||||
using DoubleType = DoubleWidthType<ElementType>;
|
||||
|
||||
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<DoubleType>(a) * b + res.get(i + j) + carry;
|
||||
|
||||
overflow |= !res.try_set(i + j, static_cast<ElementType>(t));
|
||||
carry = t >> (sizeof(ElementType) * 8);
|
||||
}
|
||||
overflow |= !res.try_set(i + m, static_cast<ElementType>(carry));
|
||||
}
|
||||
|
||||
if (overflow)
|
||||
{
|
||||
throw std::overflow_error("Multiplication overflow");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Binary operation
|
||||
template <typename TOp, AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
OpResult<TLhs, TRhs> binary_op(const TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
OpResult<TLhs, TRhs> res{};
|
||||
|
||||
try
|
||||
{
|
||||
TOp::invoke(lhs, rhs, res);
|
||||
}
|
||||
catch (const std::overflow_error&)
|
||||
{
|
||||
throw OverflowErrorOf<TLhs, TRhs>(std::move(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Compound assignment binary operation
|
||||
template <typename TOp, AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
TLhs& ca_binary_op(TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
TLhs res{};
|
||||
|
||||
try
|
||||
{
|
||||
TOp::invoke(lhs, rhs, res);
|
||||
}
|
||||
catch (const std::overflow_error&)
|
||||
{
|
||||
throw OverflowErrorOf<TLhs, TRhs>(std::move(res));
|
||||
}
|
||||
|
||||
lhs = std::move(res);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Addition operators
|
||||
|
||||
template <AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
OpResult<TLhs, TRhs> operator+(const TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
return binary_op<Addition>(lhs, rhs);
|
||||
}
|
||||
|
||||
template <AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
TLhs& operator+=(TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
return ca_binary_op<Addition>(lhs, rhs);
|
||||
}
|
||||
|
||||
// Multiplication operators
|
||||
|
||||
template <AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
OpResult<TLhs, TRhs> operator*(const TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
return binary_op<Multiplication>(lhs, rhs);
|
||||
}
|
||||
|
||||
template <AnyInt TLhs, AnyInt TRhs>
|
||||
requires ElementTypesMatch<TLhs, TRhs>
|
||||
TLhs& operator*=(TLhs& lhs, const TRhs& rhs)
|
||||
{
|
||||
return ca_binary_op<Multiplication>(lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace mp
|
||||
74
mp/storage.hpp
Normal file
74
mp/storage.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace mp
|
||||
{
|
||||
|
||||
template <typename TElem, size_t MaxSize>
|
||||
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<TElem, MaxSize> m_data;
|
||||
};
|
||||
|
||||
template <typename TElem, size_t MaxSize>
|
||||
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<TElem> m_data;
|
||||
};
|
||||
|
||||
// threshold for self-contained storage
|
||||
constexpr size_t MAX_ARRAY_BYTES = 128;
|
||||
|
||||
template <typename TElem, size_t MaxSize>
|
||||
using Container =
|
||||
std::conditional_t<(sizeof(TElem) * MaxSize > MAX_ARRAY_BYTES), VectorContainer<TElem, MaxSize>, ArrayContainer<TElem, MaxSize>>;
|
||||
|
||||
} // namespace mp
|
||||
150
mp/utils.hpp
Normal file
150
mp/utils.hpp
Normal file
@ -0,0 +1,150 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mp
|
||||
{
|
||||
|
||||
//======================== DoubleWidthType =======================//
|
||||
|
||||
template <typename T>
|
||||
struct DoubleWidth;
|
||||
|
||||
template <>
|
||||
struct DoubleWidth<uint8_t>
|
||||
{
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DoubleWidth<uint16_t>
|
||||
{
|
||||
using type = uint32_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DoubleWidth<uint32_t>
|
||||
{
|
||||
using type = uint64_t;
|
||||
};
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
template <>
|
||||
struct DoubleWidth<uint64_t>
|
||||
{
|
||||
using type = __uint128_t;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using DoubleWidthType = typename DoubleWidth<T>::type;
|
||||
|
||||
//======================== HalfWidthType =======================//
|
||||
|
||||
template <typename T>
|
||||
struct HalfWidth;
|
||||
|
||||
template <>
|
||||
struct HalfWidth<uint16_t>
|
||||
{
|
||||
using type = uint8_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HalfWidth<uint32_t>
|
||||
{
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HalfWidth<uint64_t>
|
||||
{
|
||||
using type = uint32_t;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using HalfWidthType = typename HalfWidth<T>::type;
|
||||
|
||||
//======================== Concepts =======================//
|
||||
|
||||
template <typename T>
|
||||
concept ElementSuitable = std::unsigned_integral<T>;
|
||||
|
||||
template <typename T, typename TElem = T::ElementType>
|
||||
concept AnyInt = requires(T t, TElem a, size_t i) {
|
||||
{ t[i] } -> std::convertible_to<TElem>;
|
||||
{ t.set(i, a) };
|
||||
{ t.try_set(i, a) } -> std::convertible_to<bool>;
|
||||
{ t.get(i) } -> std::convertible_to<TElem>;
|
||||
{ t.zero() };
|
||||
{ t.size_elems() } -> std::convertible_to<size_t>;
|
||||
{ T::MAX_BYTES } -> std::convertible_to<size_t>;
|
||||
{ T::MAX_ELEMS } -> std::convertible_to<size_t>;
|
||||
{ T::ELEMENT_BYTES } -> std::convertible_to<size_t>;
|
||||
{ T::LAST_ELEM_MASK } -> std::convertible_to<TElem>;
|
||||
};
|
||||
|
||||
//======================== Utils =======================//
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
using LongestElementSuitableType = uint64_t;
|
||||
#else
|
||||
using LongestElementSuitableType = HalfWidthType<size_t>;
|
||||
#endif
|
||||
|
||||
template <AnyInt TA, AnyInt TB>
|
||||
constexpr bool ElementTypesMatch = std::is_same_v<typename TA::ElementType, typename TB::ElementType>;
|
||||
|
||||
template <ElementSuitable TElem, size_t MaxBytes>
|
||||
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 <AnyInt T>
|
||||
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";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user