Initial BigInt implementation #1
303
common/bigint.hpp
Normal file
303
common/bigint.hpp
Normal 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
44
common/strings.hpp
Normal 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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user