Files
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

203 lines
5.2 KiB
C++

/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bigInt.h"
namespace sample
{
BigInt::BigInt(std::string const& str)
{
if (str.empty())
{
throw std::invalid_argument("Empty string");
}
BigInt const ten(10);
for (char const c : str)
{
if (c < '0' || c > '9')
{
throw std::invalid_argument("Invalid decimal character in BigInt string");
}
auto [mulResult, mulOverflow] = multiplyWithOverflow(*this, ten);
if (mulOverflow)
{
throw std::overflow_error("Number too large for BigInt");
}
auto [addResult, addOverflow] = addWithOverflow(mulResult, BigInt(static_cast<uint64_t>(c - '0')));
if (addOverflow)
{
throw std::overflow_error("Number too large for BigInt");
}
*this = addResult;
}
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
std::pair<BigInt, bool> BigInt::multiplyWithOverflow(BigInt const& a, BigInt const& b) noexcept
{
// Full multiplication into 2*kWordCount words
std::array<WordType, kWordCount * 2> result{};
for (uint64_t i = 0; i < kWordCount; ++i)
{
if (a.mWords[i] == 0)
{
continue;
}
WordType carry = 0;
for (uint64_t j = 0; j < kWordCount; ++j)
{
uint64_t const k = i + j;
if (k >= kWordCount * 2)
{
break;
}
// 64x64 → 128-bit multiply using four 32-bit half-words.
// Split: a = aHi*2^32 + aLo, b = bHi*2^32 + bLo
// Product = aHi*bHi*2^64 + (aHi*bLo + aLo*bHi)*2^32 + aLo*bLo
uint64_t const aLo = a.mWords[i] & 0xFFFFFFFF;
uint64_t const aHi = a.mWords[i] >> 32;
uint64_t const bLo = b.mWords[j] & 0xFFFFFFFF;
uint64_t const bHi = b.mWords[j] >> 32;
uint64_t const p0 = aLo * bLo;
uint64_t const p1 = aLo * bHi;
uint64_t const p2 = aHi * bLo;
uint64_t const p3 = aHi * bHi;
// Combine: prodLo = lower 64 bits, prodHi = upper 64 bits
uint64_t const mid = p1 + (p0 >> 32);
uint64_t const midCarry = (mid < p1) ? (uint64_t{1} << 32) : 0;
uint64_t const mid2 = mid + p2;
uint64_t const midCarry2 = (mid2 < mid) ? (uint64_t{1} << 32) : 0;
uint64_t prodLo = (mid2 << 32) | (p0 & 0xFFFFFFFF);
uint64_t prodHi = p3 + (mid2 >> 32) + midCarry + midCarry2;
// Add result[k] and carry to the 128-bit product.
prodLo += result[k];
if (prodLo < result[k])
{
++prodHi;
}
prodLo += carry;
if (prodLo < carry)
{
++prodHi;
}
result[k] = prodLo;
carry = prodHi;
}
if (i + kWordCount < kWordCount * 2)
{
result[i + kWordCount] += carry;
}
}
// Check for overflow (any non-zero word in upper half)
bool overflow = false;
for (uint64_t i = kWordCount; i < kWordCount * 2; ++i)
{
if (result[i] != 0)
{
overflow = true;
break;
}
}
// Copy lower half to result
BigInt low;
for (uint64_t i = 0; i < kWordCount; ++i)
{
low.mWords[i] = result[i];
}
return {low, overflow};
}
std::pair<BigInt, BigInt> BigInt::divideWithRemainder(BigInt const& dividend, BigInt const& divisor)
{
if (divisor.isZero())
{
throw std::domain_error("Division by zero in BigInt");
}
if (dividend < divisor)
{
return {BigInt(), dividend};
}
if (dividend == divisor)
{
return {BigInt(1), BigInt()};
}
// Binary long division algorithm
BigInt quotient;
BigInt remainder;
int32_t const highBit = dividend.getHighestSetBit();
for (int32_t i = highBit; i >= 0; --i)
{
remainder <<= 1;
if (dividend.getBit(i))
{
remainder.setBit(0, true);
}
if (remainder >= divisor)
{
remainder -= divisor;
quotient.setBit(i, true);
}
}
return {quotient, remainder};
}
std::string BigInt::toString() const
{
if (isZero())
{
return "0";
}
std::string result;
BigInt tmp = *this;
BigInt const ten(10);
while (!tmp.isZero())
{
auto [q, r] = divideWithRemainder(tmp, ten);
result += static_cast<char>('0' + r.mWords[0]);
tmp = q;
}
std::reverse(result.begin(), result.end());
return result;
}
} // namespace sample