// Copyright (c) 2008-2023 the Urho3D project // License: MIT #include "BigInt.h" #include "../Container/Pair.h" #include "../Core/StringUtils.h" #include "../DebugNew.h" namespace Urho3D { #if true static constexpr BigInt::Digit BASE = 1000'000'000; // 10^9 // Number of decimal digits in each magnitude_ element static constexpr i32 BASE_DIGITS = 9; #else // For tests static constexpr BigInt::Digit BASE = 10; static constexpr i32 BASE_DIGITS = 1; #endif // Compare magnitudes static bool FirstIsLess(const Vector& first, const Vector& second) { if (first.Size() != second.Size()) return first.Size() < second.Size(); for (i32 i = first.Size() - 1; i >= 0; --i) { if (first[i] != second[i]) return first[i] < second[i]; } return false; // first == b } // a + b static Vector SumMagnitudes(const Vector& a, const Vector& b) { Vector ret; i32 maxSize = Max(a.Size(), b.Size()); ret.Resize(maxSize, 0); for (i32 i = 0; i < maxSize; ++i) { BigInt::Digit a_element = i < a.Size() ? a[i] : 0; BigInt::Digit b_element = i < b.Size() ? b[i] : 0; BigInt::Digit sum = a_element + b_element; ret[i] += sum; // ret[i] can be not zero while (ret[i] >= BASE) { ret[i] -= BASE; // Use div & mod instead loop? if (i + 1 < ret.Size()) ++ret[i + 1]; else ret.Push(1); } } return ret; } // a - b (a must be >= b) static Vector DiffMagnitudes(const Vector& a, const Vector& b) { Vector ret; ret.Resize(a.Size(), 0); for (i32 i = 0; i < a.Size(); ++i) { BigInt::Digit a_element = a[i]; BigInt::Digit b_element = i < b.Size() ? b[i] : 0; BigInt::Digit diff = a_element - b_element; ret[i] += diff; // ret[i] can be not zero while (ret[i] < 0) { ret[i] += BASE; assert(i + 1 < ret.Size()); --ret[i + 1]; } } // Remove leading zeros while (ret.Size() >= 2 && ret.Back() == 0) ret.Pop(); return ret; } // Return quotient and remainder. // If denominator == 0, then return {0, 0} Pair DivMod(BigInt numerator, BigInt denominator) { if (denominator == 0) return {0, 0}; BigInt absNum = Abs(numerator); BigInt absDenom = Abs(denominator); BigInt quotient = 0; BigInt remainder = absNum; // The Simplest and slowest way while (remainder >= absDenom) { ++quotient; remainder -= absDenom; String strQ = quotient.ToString(); String strRem = remainder.ToString(); int a = 0; } // https://en.cppreference.com/w/cpp/language/operator_arithmetic // (a/b)*b + a%b == a // 7/3 = {2, 1} | 2*3 + 1 == 7 // -7/3 = {-2, -1} | -2*3 + -1 == -7 // 7/-3 = {-2, 1} | -2*-3 + 1 == 7 // -7/-3 = {2, -1} | 2*-3 + -1 == -7 if (numerator.IsPositive() != denominator.IsPositive()) quotient = -quotient; if (numerator.IsNegative()) remainder = -remainder; return {quotient, remainder}; } BigInt::BigInt() { // Init as zero positive_ = true; magnitude_.Resize(1, 0); } BigInt::BigInt(const String& str) { if (str.Empty()) { // Init as zero positive_ = true; magnitude_.Resize(1, 0); return; } i32 firstDigitPos; if (str[0] == '-') { positive_ = false; firstDigitPos = 1; } else if (str[0] == '+') { positive_ = true; firstDigitPos = 1; } else { positive_ = true; firstDigitPos = 0; } // Skip leading zeros for (i32 i = firstDigitPos; i < (i32)str.Length(); ++i) { if (str[i] != '0') break; ++firstDigitPos; } i32 lastDigitPos = -1; for (i32 i = firstDigitPos; i < (i32)str.Length(); ++i) { if (!IsDigit(str[i])) break; lastDigitPos = i; } if (lastDigitPos == -1) { // Init as zero positive_ = true; magnitude_.Resize(1, 0); return; } i32 numDigits = lastDigitPos - firstDigitPos + 1; magnitude_.Reserve(numDigits / BASE_DIGITS + 1); // Retrieve chunks of 9 digits in reverse order and save to single i32 i32 i = lastDigitPos - BASE_DIGITS + 1; for (; i > firstDigitPos; i -= BASE_DIGITS) { String chunk = str.Substring(i, BASE_DIGITS); magnitude_.Push(ToI64(chunk)); // To Digit } String lastChunk = str.Substring(firstDigitPos, BASE_DIGITS + i - firstDigitPos); magnitude_.Push(ToI64(lastChunk)); // To Digit } BigInt::BigInt(i32 value) { positive_ = (value >= 0); value = Urho3D::Abs(value); while (value != 0) { Digit mod = value % BASE; magnitude_.Push(mod); value /= BASE; } if (!magnitude_.Size()) // value == 0 magnitude_.Push(0); } BigInt::BigInt(i64 value) { positive_ = (value >= 0); value = Urho3D::Abs(value); while (value != 0) { Digit mod = value % BASE; magnitude_.Push(mod); value /= BASE; } if (!magnitude_.Size()) // value == 0 magnitude_.Push(0); } BigInt::BigInt(u32 value) { positive_ = true; while (value != 0) { Digit mod = value % BASE; magnitude_.Push(mod); value /= BASE; } if (!magnitude_.Size()) // value == 0 magnitude_.Push(0); } BigInt::BigInt(u64 value) { positive_ = true; while (value != 0) { Digit mod = (Digit)(value % BASE); magnitude_.Push(mod); value /= BASE; } if (!magnitude_.Size()) // value == 0 magnitude_.Push(0); } bool BigInt::IsZero() const { assert(magnitude_.Size() > 0); if (magnitude_.Size() == 1 && magnitude_[0] == 0) { assert(positive_); return true; } return false; } bool BigInt::operator <(const BigInt& rhs) const { if (positive_ != rhs.positive_) return !positive_; if (positive_) return FirstIsLess(magnitude_, rhs.magnitude_); else return FirstIsLess(rhs.magnitude_, magnitude_); } String BigInt::ToString() const { assert(magnitude_.Size() > 0); String ret; if (!positive_) ret = "-"; // Beginning of the number without leading zeros i32 i = (i32)magnitude_.Size() - 1; ret += String(magnitude_[i]); --i; // Another chunks with leading zeros for (; i >= 0; --i) { String str(magnitude_[i]); if (str.Length() < BASE_DIGITS) { String zeros('0', BASE_DIGITS - str.Length()); str = zeros + str; } ret += str; } return ret; } BigInt BigInt::operator +(const BigInt& rhs) const { BigInt ret; if (positive_ == rhs.positive_) { ret.positive_ = positive_; ret.magnitude_ = SumMagnitudes(magnitude_, rhs.magnitude_); } else { if (FirstIsLess(magnitude_, rhs.magnitude_)) { ret.positive_ = rhs.positive_; ret.magnitude_ = DiffMagnitudes(rhs.magnitude_, magnitude_); } else { ret.positive_ = positive_; ret.magnitude_ = DiffMagnitudes(magnitude_, rhs.magnitude_); } } return ret; } BigInt BigInt::operator -(const BigInt& rhs) const { BigInt ret; if (positive_ != rhs.positive_) { ret.positive_ = positive_; ret.magnitude_ = SumMagnitudes(magnitude_, rhs.magnitude_); } else { if (FirstIsLess(magnitude_, rhs.magnitude_)) { ret.positive_ = !rhs.positive_; ret.magnitude_ = DiffMagnitudes(rhs.magnitude_, magnitude_); } else { ret.positive_ = positive_; ret.magnitude_ = DiffMagnitudes(magnitude_, rhs.magnitude_); } } return ret; } BigInt BigInt::operator *(const BigInt& rhs) const { BigInt ret; ret.magnitude_.Resize(magnitude_.Size() + rhs.magnitude_.Size(), 0); if (positive_ == rhs.positive_) ret.positive_ = true; else ret.positive_ = false; for (i32 this_index = 0; this_index < magnitude_.Size(); ++this_index) { for (i32 rhs_index = 0; rhs_index < rhs.magnitude_.Size(); ++rhs_index) ret.magnitude_[this_index + rhs_index] += magnitude_[this_index] * rhs.magnitude_[rhs_index]; } for (i32 i = 0; i < ret.magnitude_.Size() - 1; ++i) { ret.magnitude_[i + 1] += ret.magnitude_[i] / BASE; ret.magnitude_[i] %= BASE; } // Remove leading zeros while (ret.magnitude_.Size() >= 2 && ret.magnitude_.Back() == 0) ret.magnitude_.Pop(); return ret; } BigInt BigInt::operator /(const BigInt& rhs) const { Pair divMod = DivMod(*this, rhs); return divMod.first_; } BigInt BigInt::operator %(const BigInt& rhs) const { Pair divMod = DivMod(*this, rhs); return divMod.second_; } BigInt BigInt::operator -() const { BigInt ret = *this; if (!ret.IsZero()) ret.positive_ = !ret.positive_; return ret; } BigInt& BigInt::operator +=(const BigInt& rhs) { BigInt result = *this + rhs; Swap(this->positive_, result.positive_); Swap(this->magnitude_, result.magnitude_); return *this; } BigInt& BigInt::operator -=(const BigInt& rhs) { BigInt result = *this - rhs; Swap(this->positive_, result.positive_); Swap(this->magnitude_, result.magnitude_); return *this; } BigInt& BigInt::operator *=(const BigInt& rhs) { BigInt result = *this * rhs; Swap(this->positive_, result.positive_); Swap(this->magnitude_, result.magnitude_); return *this; } BigInt& BigInt::operator /=(const BigInt& rhs) { BigInt result = *this / rhs; Swap(this->positive_, result.positive_); Swap(this->magnitude_, result.magnitude_); return *this; } BigInt& BigInt::operator %=(const BigInt& rhs) { BigInt result = *this % rhs; Swap(this->positive_, result.positive_); Swap(this->magnitude_, result.magnitude_); return *this; } } // namespace Urho3D