Initial BigInt implementation #1

Merged
me merged 2 commits from bigint into main 2025-09-08 21:31:52 -06:00
2 changed files with 347 additions and 0 deletions
Showing only changes of commit 185fcd058b - Show all commits

303
common/bigint.hpp Normal file
View File

@ -0,0 +1,303 @@
#pragma once
#include <algorithm>
#include <cstdint>
#include <expected>
#include <ranges>
#include <string>
#include <string_view>
#include <vector>
#include <strings.hpp>
struct BigInt {
static constexpr uint32_t Base = 100;
friend BigInt operator+(const BigInt &, const BigInt &);
friend BigInt operator-(const BigInt &, const BigInt &);
friend BigInt operator*(const BigInt &, const BigInt &);
friend BigInt operator/(const BigInt &, const BigInt &);
static std::expected<BigInt, std::string> fromString(std::string_view value) {
BigInt bi = {};
uint8_t cval = 0;
value = trim(value, isSpace);
if (value.empty()) {
return std::unexpected{"Invalid value, no digits found"};
}
if (value.front() == '-') {
value.remove_prefix(1);
bi.isNegative = true;
} else if (value.front() == '+') {
value.remove_prefix(1);
}
if (value.empty()) {
return std::unexpected{"Invalid value, no digits found"};
}
if (std::ranges::any_of(
value, [](uint8_t ch) -> bool { return !std::isdigit(ch); })) {
return std::unexpected{"Invalid value, contains non-digits"};
}
bi.digits.reserve((value.size() / 2) + 1);
auto it = value.begin();
if ((value.size() % 2) == 1) {
bi.digits.push_back(uint8_t(*it++ - '0'));
}
while (it != value.end()) {
cval = uint8_t((*it++ - '0') * 10);
cval += uint8_t(*it++ - '0');
bi.digits.push_back(cval);
}
std::ranges::reverse(bi.digits);
bi.trimLeadingZeroes();
if (bi.isZero()) {
bi.isNegative = false;
}
return bi;
}
bool isZero() noexcept { return digits.empty(); }
BigInt() : isNegative(false), digits() {}
BigInt(BigInt &&) = default;
BigInt(const BigInt &) = default;
BigInt &operator=(BigInt &&) = default;
BigInt &operator=(const BigInt &) = default;
~BigInt() = default;
bool isNegative;
std::vector<uint8_t> digits;
private:
void trimLeadingZeroes() noexcept {
while (!this->digits.empty() && this->digits.back() == 0) {
this->digits.pop_back();
}
}
static BigInt addMagnitudes(const BigInt &lhs, const BigInt &rhs) {
BigInt res = {};
size_t it = 0;
uint8_t carry = 0;
auto minL = std::min(lhs.digits.size(), rhs.digits.size());
auto maxL = std::max(lhs.digits.size(), rhs.digits.size());
const auto &maxC = [&] -> const BigInt & {
if (lhs.digits.size() > rhs.digits.size()) {
return lhs;
}
return rhs;
}();
res.digits.reserve(maxL + 1);
for (it = 0; it < minL; ++it) {
uint8_t csum = lhs.digits[it] + rhs.digits[it] + carry;
carry = csum / Base;
csum %= Base;
res.digits.push_back(csum);
}
for (; it < maxL; ++it) {
uint8_t csum = maxC.digits[it] + carry;
carry = csum / Base;
csum %= Base;
res.digits.push_back(csum);
}
if (carry != 0) {
res.digits.push_back(carry);
}
return res;
}
static BigInt subMagnitudes(const BigInt &lhs, const BigInt &rhs) {
BigInt res = {};
size_t it = 0;
uint8_t borrow = 0;
res.digits.reserve(lhs.digits.size() + 1);
for (it = 0; it < rhs.digits.size(); ++it) {
int8_t csub = lhs.digits[it] - rhs.digits[it] - borrow;
if (csub < 0) {
csub += Base;
borrow = 1;
} else {
borrow = 0;
}
res.digits.push_back(uint8_t(csub));
}
for (; it < lhs.digits.size(); ++it) {
int8_t csub = lhs.digits[it] - borrow;
if (csub < 0) {
csub += Base;
borrow = 1;
} else {
borrow = 0;
}
res.digits.push_back(uint8_t(csub));
}
res.trimLeadingZeroes();
return res;
}
static BigInt multiplyMagnitudes(const BigInt &lhs, const BigInt &rhs) {
const auto &maxC = [&] -> const BigInt & {
if (lhs.digits.size() > rhs.digits.size()) {
return lhs;
}
return rhs;
}();
const auto &minC = [&] -> const BigInt & {
if (lhs.digits.size() <= rhs.digits.size()) {
return lhs;
}
return rhs;
}();
std::vector<uint32_t> accs(lhs.digits.size() + rhs.digits.size() + 1, 0);
for (const auto &[bi, b] : std::views::enumerate(minC.digits)) {
uint32_t carry = 0;
for (const auto &[ti, t] : std::views::enumerate(maxC.digits)) {
uint32_t cmul = b * t + carry;
carry = cmul / Base;
cmul %= Base;
accs[size_t(ti + bi)] += cmul;
}
if (carry != 0) {
accs[maxC.digits.size() + size_t(bi)] += carry;
}
}
BigInt res{};
uint32_t carry = 0;
res.digits.reserve(accs.size() + 2);
for (auto &d : accs) {
d += carry;
carry = d / Base;
res.digits.push_back(d % Base);
}
while (carry != 0) {
res.digits.push_back(carry % Base);
carry /= Base;
}
res.trimLeadingZeroes();
return res;
}
static std::strong_ordering compareMagnitudes(const BigInt &lhs,
const BigInt &rhs) noexcept {
namespace vw = std::views;
if (lhs.digits.size() > rhs.digits.size()) {
return std::strong_ordering::greater;
}
if (rhs.digits.size() > lhs.digits.size()) {
return std::strong_ordering::less;
}
for (const auto &[l, r] : vw::zip(lhs.digits, rhs.digits) | vw::reverse) {
if (l > r) {
return std::strong_ordering::greater;
}
if (r > l) {
return std::strong_ordering::less;
}
}
return std::strong_ordering::equal;
}
};
inline BigInt operator+(const BigInt &lhs, const BigInt &rhs) {
if (lhs.isNegative == rhs.isNegative) {
BigInt res = BigInt::addMagnitudes(lhs, rhs);
res.isNegative = lhs.isNegative;
return res;
}
auto comp = BigInt::compareMagnitudes(lhs, rhs);
if (comp == std::strong_ordering::greater) {
BigInt res = BigInt::subMagnitudes(lhs, rhs);
res.isNegative = lhs.isNegative;
return res;
}
if (comp == std::strong_ordering::less) {
BigInt res = BigInt::subMagnitudes(rhs, lhs);
res.isNegative = rhs.isNegative;
return res;
}
return BigInt{};
}
inline BigInt operator-(const BigInt &lhs, const BigInt &rhs) {
BigInt res = rhs;
if (!res.isZero()) {
res.isNegative = !res.isNegative;
}
return lhs + res;
}
inline BigInt operator*(const BigInt &lhs, const BigInt &rhs) {
BigInt res = BigInt::multiplyMagnitudes(lhs, rhs);
res.isNegative = lhs.isNegative ^ rhs.isNegative;
if (res.isZero()) {
res.isNegative = false;
}
return res;
}
inline BigInt operator/(const BigInt &lhs, const BigInt &rhs) {
BigInt res = {};
return res;
}

44
common/strings.hpp Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <cctype>
#include <functional>
#include <string_view>
static inline bool isSpace(char ch) {
return std::isspace(ch);
}
static std::string_view ltrim(std::string_view str,
std::function<bool(char)> filter = isSpace) {
auto it = str.begin();
while (it != str.end()) {
if (!filter(*it)) {
break;
}
++it;
}
return {it, str.end()};
}
static std::string_view rtrim(std::string_view str,
std::function<bool(char)> filter = isSpace) {
auto it = str.rbegin();
while (it != str.rend()) {
if (!filter(*it)) {
break;
}
++it;
}
return {str.begin(), it.base()};
}
static std::string_view trim(std::string_view str,
std::function<bool(char)> filter = isSpace) {
return ltrim(rtrim(str, filter), filter);
}