diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e5761e9 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,48 @@ +BUILD ?= debug +TARGET := test_bigint +CXX := g++ +CXXFLAGS := -std=c++23 +LDFLAGS := +CXXFLAGS += -MMD -MP +CXXFLAGS += -fstrict-aliasing -Wstrict-aliasing \ + -Wold-style-cast +# 预处理标志 +CPPFLAGS := + +ifeq ($(BUILD), debug) +# 为了获取完整的栈回溯 +CXXFLAGS += -g -fno-omit-frame-pointer +# AddressSanitizer 是 LeakSanitizer 的超集。 +# 检测内存的 LeakSanitizer 是集成在 AddressSanitizer 中的一个相对独立的工具,它 +# 工作在检查过程的最后阶段。 +CXXFLAGS += -fsanitize=address +LDFLAGS += -static-libasan -fsanitize=address +# UBSanitizer 也变成运行时检查 +CXXFLAGS += -fsanitize=undefined +LDFLAGS += -fsanitize=undefined +else +# assert() 开关 +CXXFLAGS += -DNDEBUG +endif + +SRCS := $(wildcard *.cpp) +OBJS := $(SRCS:.cpp=.o) +ASMS := $(SRCS:.cpp=.s) +DEPS := $(OBJS:.o=.d) + +.PHONY: all clean insight + +all: $(TARGET) +$(TARGET): $(OBJS) + $(CXX) $(LDFLAGS) $^ -o $@ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ -MF $*.d + +-include $(DEPS) + +insight: $(SRCS) + $(CXX) $(CXXFLAGS) -S -fverbose-asm $^ + +clean: + rm -rf $(TARGET) $(OBJS) $(ASMS) $(DEPS) diff --git a/src/big_int.hpp b/src/big_int.hpp new file mode 100644 index 0000000..8c96138 --- /dev/null +++ b/src/big_int.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include "big_uint.hpp" + +#include +#include +#include +#include + +namespace big_int { + +template +struct big_int : big_uint::big_uint +{ + constexpr bool sign() const noexcept + { + return detail::big_uint::msb(*this); + } +}; + +template +constexpr big_int operator+( + const big_int& u, + const big_int& v) noexcept +{ + using bint = big_int; + using buint = big_uint::big_uint; + auto& up = static_cast(u); + auto& vp = static_cast(v); + return static_cast(up + vp); +} + +template +constexpr big_int operator-( + const big_int& u, + const big_int& v) noexcept +{ + using bint = big_int; + using buint = big_uint::big_uint; + auto& up = static_cast(u); + auto& vp = static_cast(v); + return static_cast(up - vp); +} + +/* +template +template +inline constexpr auto big_uint::operator-( + const big_uint& u) const + -> big_uint Np) ? N : Np> +{ + auto w = big_uint Np) ? N : Np>{ 0 }; + detail::big_uint::substract(w, *this, u); + return w; +} + +template +template +inline constexpr auto big_uint::operator*( + const big_uint& u) const + -> big_uint Np) ? N : Np> +{ + auto w = big_uint Np) ? N : Np>{ 0 }; + detail::big_uint::multiply(w, *this, u); + return w; +} + +*/ +template +constexpr big_int from_string(std::string_view s) noexcept +{ + auto v = big_int{ 0 }; + bool sign = (s[0] == '-'); + detail::big_uint::from_string(v, sign ? s.substr(1) : s); + if (sign) + detail::big_uint::two_complement(v, v); + return v; +} +} + +template +struct std::formatter> +{ + constexpr auto parse(std::format_parse_context& context) + { + return context.begin(); + } + + auto format(const big_int::big_int& v, std::format_context& context) const + { + std::string s; + char sign = v.sign() ? '-' : '+'; + if (v.sign()) { + big_uint::big_uint w; + detail::big_uint::two_complement(w, v); + detail::big_uint::to_string(s, w); + } else { + detail::big_uint::to_string(s, v); + } + return std::format_to(context.out(), "{}{}", sign, s); + } +}; diff --git a/src/big_uint.hpp b/src/big_uint.hpp new file mode 100644 index 0000000..6add9bc --- /dev/null +++ b/src/big_uint.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include "detail/big_uint.hpp" + +#include +#include +#include +#include +#include + +namespace big_uint { + +template +struct big_uint : detail::big_uint::big_uint +{ +}; + +template +constexpr big_uint operator+( + const big_uint& u, + const big_uint& v) noexcept +{ + big_uint w; + detail::big_uint::add(w, u, v); + return w; +} + +template +constexpr big_uint operator-( + const big_uint& u, + const big_uint& v) noexcept +{ + big_uint w; + detail::big_uint::substract(w, u, v); + return w; +} + +template +constexpr big_uint two_complement(const big_uint& u) noexcept +{ + big_uint v; + detail::big_uint::two_complement(v, u); + return v; +} + +template +constexpr big_uint operator*( + const big_uint& u, + const big_uint& v) noexcept +{ + auto w = big_uint{ 0 }; + detail::big_uint::multiply(w, u, v); + return w; +} + +/* +*/ +template +constexpr big_uint from_string(std::string_view s) noexcept +{ + auto v = big_uint{ 0 }; + detail::big_uint::from_string(v, s); + return v; +} +} + +template +struct std::formatter> +{ + constexpr auto parse(std::format_parse_context& context) + { + return context.begin(); + } + + auto format(const big_uint::big_uint& v, std::format_context& context) const + { + std::string s; + detail::big_uint::to_string(s, v); + return std::format_to(context.out(), "{}", s); + } +}; diff --git a/src/detail/big_uint.hpp b/src/detail/big_uint.hpp new file mode 100644 index 0000000..907028b --- /dev/null +++ b/src/detail/big_uint.hpp @@ -0,0 +1,203 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace detail::big_uint { + +consteval std::size_t ceil(std::size_t value, std::size_t base) +{ + return (value + base - 1) / base; +} + +template +struct big_uint : std::array +{ + using digit_type = T; + static constexpr std::size_t digit_nbits = sizeof(T) * CHAR_BIT; + static_assert(N % digit_nbits == 0); + static constexpr std::size_t digit_nhbytes = (sizeof(T) << 1u); + static constexpr T digit_mask = static_cast(-1); +}; + +template +constexpr bool msb(const big_uint& u) noexcept +{ + constexpr unsigned pos_msb = u.digit_nbits - 1; + return (u.back() >> pos_msb & 0x1u); +} + +template +constexpr bool add_digit( + big_uint& u, + const big_uint& v, + T w) noexcept +{ + T c = w; + for (unsigned i = 0u; i < v.size() && c > 0; ++i) { + T temp = v[i]; + u[i] = temp + c; + c = u[i] < temp; + } + + return c; +} + +template +constexpr void two_complement( + big_uint& u, + const big_uint& v) noexcept +{ + for (unsigned i = 0u; i < v.size(); ++i) { + u[i] = ~v[i]; + } + + add_digit(u, u, 1u); +} + +template +constexpr bool add( + big_uint& u, + const big_uint& v, + const big_uint& w) noexcept +{ + T c = 0u; + for (unsigned i = 0u; i < u.size(); ++i) { + u[i] = v[i] + w[i] + c; + c = (!c && (u[i] < v[i])) || (c && (u[i] <= v[i])); + } + + return c; +} + +template +constexpr bool substract( + big_uint& u, + const big_uint& v, + const big_uint& w) noexcept +{ + T b = 0u; + for (unsigned i = 0u ; i < v.size(); ++i) { + u[i] = v[i] - w[i] - b; + b = (!b && u[i] > v[i]) || (b && u[i] >= v[i]); + } + + return b; +} + +template +concept digit_promotable = std::unsigned_integral + && (std::same_as || std::same_as + || std::same_as || std::same_as); + +template +struct large_digit_from_digit; + +#define DEFINE_LARGE_DIGIT_FROM_DIGIT(digit, large_digit) \ + template<> \ + struct large_digit_from_digit \ + { \ + using type = large_digit; \ + } + +DEFINE_LARGE_DIGIT_FROM_DIGIT(std::uint8_t, std::uint16_t); +DEFINE_LARGE_DIGIT_FROM_DIGIT(std::uint16_t, std::uint32_t); +DEFINE_LARGE_DIGIT_FROM_DIGIT(std::uint32_t, std::uint64_t); +DEFINE_LARGE_DIGIT_FROM_DIGIT(std::uint64_t, unsigned __int128); + +template +using large_digit = large_digit_from_digit::type; + +template +constexpr large_digit digit_multiply(T a, T b) noexcept +{ + return static_cast>(a) * b; +} + +template +constexpr void multiply( + big_uint& u, + const big_uint& v, + const big_uint& w) noexcept +{ + std::size_t size = u.size(); + for (unsigned i = 0; i < size; ++i) { + T c = 0u; + for (unsigned j = 0, k = i; j < size && k < size; ++j, ++k) { + large_digit r = digit_multiply(v[i], w[j]); + r = r + u[k] + c; + u[k] = u.digit_mask & r; + c = u.digit_mask & (r >> u.digit_nbits); + } + } +} + +/* +template +inline constexpr void complement(big_uint& u, const big_uint& v) noexcept +{ + for (std::size_t i = 0u; i < v.size(); ++i) { + u[i] = ~v[i]; + } + + if constexpr (u.bitmask_msb > 0) { + u.back() &= u.bitmask_msb; + } +} + +inline constexpr bool compile_time_assert(bool pred) noexcept +{ + return true; +} + +*/ +#define ASSERT(pred) \ + do { \ + if constexpr (std::is_constant_evaluated()) { \ + static_assert((pred)); \ + } else { \ + assert(pred); \ + } \ + } while (0) + +template +constexpr void from_string(big_uint& u, std::string_view s) noexcept +{ + unsigned i = 0; + for (auto rit = std::crbegin(s); rit != std::crend(s); ++rit) { + char c = *rit; + T v; + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'z') { + v = c - 'a' + 10; + } else if (c >= 'A' && c <= 'Z') { + v = c - 'A' + 10; + } else { + // how? + } + + u[i / u.digit_nhbytes] |= v << ((i % u.digit_nhbytes) * 4u); + i++; + } +} + +template +constexpr void to_string(std::string& s, const big_uint& u) +{ + for (auto rit = std::crbegin(u); rit != std::crend(u); ++rit) { + s.append(std::format("{1:0>{0}x}", sizeof(T) << 1u, *rit)); + } +} + +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..dba60e7 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,111 @@ +#include "big_int.hpp" +#include "big_uint.hpp" + +#include +#include +#include + +#include + +using namespace std::literals; + +int main(int argc, char *arg[]) +{ + constexpr big_int::big_int u { 5 }; + constexpr big_int::big_int v { 1 }; + constexpr auto w = u + v; + std::cout << std::format("{}", u) << std::endl; + std::cout << std::format("{}", v) << std::endl; + std::cout << std::format("{}", w) << std::endl; + constexpr auto y = big_int::from_string("-123456789abcdef123456fafafafff"sv); + constexpr auto z = big_int::from_string("-123456789abcdef123456fafafafff"sv); + constexpr auto x = y + z; + std::cout << std::format("{}", x) << std::endl; + + constexpr big_uint::big_uint uu { 5 }; + constexpr big_uint::big_uint uv { 1 }; + constexpr auto uw = uu + uv; + std::cout << std::format(" {}", uu) << std::endl; + std::cout << std::format(" {}", uv) << std::endl; + std::cout << std::format(" {}", uw) << std::endl; + constexpr auto uy = big_uint::from_string("123456789abcdef123456fafafafff"sv); + constexpr auto uz = big_uint::from_string("123456789abcdef123456fafafafff"sv); + constexpr auto ux = uy + uz; + std::cout << std::format(" {}", uy + uz) << std::endl; + constexpr auto uyy = big_uint::from_string("ffffffffffffffffffffffffffffff"sv); + constexpr auto uzz = big_uint::from_string("ffffffffffffffffffffffffffffff"sv); + constexpr auto uxx = uyy + uzz; + std::cout << std::format(" {}", uxx) << std::endl; + + std::cout << "==" << std::endl; + constexpr auto a = big_uint::from_string("86f6050d979544fe39e7142446a40b3"sv); + constexpr auto b = big_uint::from_string("ab5a0c4d00312f392c0edc2878d6d8a"sv); + constexpr auto c = b - a; + constexpr auto d = a - b; + constexpr auto e = big_uint::two_complement(d); + std::cout << std::format(" {}", a) << std::endl; + std::cout << std::format(" {}", b) << std::endl; + std::cout << std::format(" {}", c) << std::endl; + std::cout << std::format(" {}", d) << std::endl; + std::cout << std::format(" {}", e) << std::endl; + + std::cout << "==" << std::endl; + constexpr auto ap = big_uint::from_string("0"sv); + constexpr auto bp = big_uint::from_string("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"sv); + constexpr auto cp = bp - ap; + constexpr auto dp = ap - bp; + constexpr auto ep = big_uint::two_complement(dp); + std::cout << std::format(" {}", ap) << std::endl; + std::cout << std::format(" {}", bp) << std::endl; + std::cout << std::format(" {}", cp) << std::endl; + std::cout << std::format(" {}", dp) << std::endl; + std::cout << std::format(" {}", ep) << std::endl; + + std::cout << "==" << std::endl; + constexpr auto k = big_uint::from_string("0645d91e97ca853ad4fa29c"sv); + constexpr auto l = big_uint::from_string("33b2e3c9fd0803ce7ffffff"sv); + constexpr auto ik = big_int::from_string("0645d91e97ca853ad4fa29c"sv); + constexpr auto il = big_int::from_string("33b2e3c9fd0803ce7ffffff"sv); + constexpr auto kl = k - l; + constexpr auto lk = l - k; + constexpr auto klp = big_uint::two_complement(kl); + constexpr auto ikl = ik - il; + constexpr auto ilk = il - ik; + constexpr auto iklp = big_uint::two_complement(ikl); + std::cout << std::format(" {}", k) << std::endl; + std::cout << std::format(" {}", l) << std::endl; + std::cout << std::format(" {}", kl) << std::endl; + std::cout << std::format(" {}", lk) << std::endl; + std::cout << std::format(" {}", klp) << std::endl; + std::cout << "----" << std::endl; + std::cout << std::format(" {}", ik) << std::endl; + std::cout << std::format(" {}", il) << std::endl; + std::cout << std::format(" {}", ikl) << std::endl; + std::cout << std::format(" {}", ilk) << std::endl; + std::cout << std::format(" {}", iklp) << std::endl; + + std::cout << "==" << std::endl; + constexpr auto s = big_uint::from_string("5e4b9f99b2b05d64"sv); + constexpr auto t = big_uint::from_string("33b2e3c9fd0803ce"sv); + constexpr auto sp = big_uint::from_string("0645d91e97ca853ad4fa29c"sv); + constexpr auto tp = big_uint::from_string("33b2e3c9fd0803ce7ffffff"sv); + constexpr auto sxt = s * t; + constexpr auto kxl = k * l; + constexpr auto spxtp = sp * tp; + std::cout << std::format(" {}", sxt) << std::endl; + std::cout << std::format(" {}", kxl) << std::endl; + std::cout << std::format(" {}", spxtp) << std::endl; + + /* + std::cout << std::format("{} {} {} {} {:b}", u[0], u.size(), u.word_nbits, + u.word_hbytes, w.bitmask_msb) << std::endl; + std::cout << std::format("{} {}", w[0], w.size()) << std::endl; + constexpr auto x = u - v; + std::cout << std::format("{}", x) << std::endl; + constexpr auto y = v - u; + std::cout << std::format("{}", y) << std::endl; + z = z + z + z + z; + std::cout << std::format("{}", z) << std::endl; + */ + return 0; +}