New era
This commit is contained in:
parent
ac10af30da
commit
08ea929084
@ -4,3 +4,5 @@ PointerAlignment: Left
|
|||||||
IndentWidth: 4 # spaces per indent level
|
IndentWidth: 4 # spaces per indent level
|
||||||
TabWidth: 4 # width of a tab character
|
TabWidth: 4 # width of a tab character
|
||||||
UseTab: Never # options: Never, ForIndentation, Alwayss
|
UseTab: Never # options: Never, ForIndentation, Alwayss
|
||||||
|
BreakTemplateDeclarations: Yes
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
.vs/
|
||||||
build/
|
build/
|
||||||
|
|||||||
71
main.cpp
71
main.cpp
@ -1,31 +1,84 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "mp.hpp"
|
//#include "mp.hpp"
|
||||||
|
#include "mp/int.hpp"
|
||||||
|
#include "mp/storage.hpp"
|
||||||
|
#include "mp/math.hpp"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
static void PrintInt(const char* name, const T& val)
|
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()
|
int main()
|
||||||
{
|
{
|
||||||
// mp::Int a{0xDEADBEEFDEADF154, 0x0123456789ABCDEF, 0x1111222233334444};
|
// mp::Int a{0xDEADBEEFDEADF154, 0x0123456789ABCDEF, 0x1111222233334444};
|
||||||
mp::Int<4> a{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF};
|
mp::Int<32> a{0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
||||||
mp::Int<5> b{0x55};
|
mp::Int<1> b{0x55};
|
||||||
|
|
||||||
PrintInt("a", a);
|
PrintInt("a", a);
|
||||||
PrintInt("b", b);
|
PrintInt("b", b);
|
||||||
|
|
||||||
auto c = a + b;
|
auto c = a + b;
|
||||||
|
//auto c = binary_opa, b);
|
||||||
PrintInt("c", c);
|
PrintInt("c", c);
|
||||||
|
|
||||||
a += mp::Int<1>{1};
|
//a += mp::Int<1>{1};
|
||||||
PrintInt("a", a);
|
//PrintInt("a", a);
|
||||||
|
|
||||||
a -= mp::Int<1>{1};
|
//a -= mp::Int<1>{1};
|
||||||
PrintInt("a", a);
|
//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