/* * 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. */ #ifndef TRT_SAMPLE_BIG_INT_H #define TRT_SAMPLE_BIG_INT_H #include #include #include #include #include #include #include namespace sample { //! //! \class BigInt //! \brief A class for arbitrary-precision unsigned integers (8192 bits). //! //! This class provides support for very large unsigned integers, primarily used //! for counting and indexing in build path expression expansion where the number //! of combinations can be astronomically large (e.g., 2^1000 combinations). //! //! Key operations: //! - Construction from uint64_t or decimal string //! - Comparison operators for loop termination //! - Increment operator for loop counting //! - Division/modulo for mixed-radix index decomposition //! - String conversion for display //! class BigInt { public: //! Number of bits in the integer (8192 = 128 * 64) static constexpr uint64_t kBitCount = 8192; //! Number of 64-bit words static constexpr uint64_t kWordCount = kBitCount / 64; // 128 words using WordType = uint64_t; //! \brief Default constructor. Initializes to zero. constexpr BigInt() noexcept = default; //! \brief Construct from a 64-bit unsigned integer. //! \param[in] value The initial value. constexpr BigInt(uint64_t value) noexcept { mWords[0] = value; } //! \brief Construct from a decimal string. //! \param[in] str The decimal string representation. //! \throws std::invalid_argument If the string is empty or contains invalid characters. //! \throws std::overflow_error If the number is too large. explicit BigInt(std::string const& str); // Default copy and move operations constexpr BigInt(BigInt const&) noexcept = default; constexpr BigInt& operator=(BigInt const&) noexcept = default; constexpr BigInt(BigInt&&) noexcept = default; constexpr BigInt& operator=(BigInt&&) noexcept = default; //! \brief Check if the value is zero. //! \return True if zero. constexpr bool isZero() const noexcept { for (uint64_t i = 0; i < kWordCount; ++i) { if (mWords[i] != 0) { return false; } } return true; } //! \brief Get the bit value at a specific position. //! \param[in] pos The bit position (0 = LSB). //! \return The bit value. constexpr bool getBit(uint64_t pos) const noexcept { if (pos >= kBitCount) { return false; } uint64_t const wordIdx = pos / 64; uint64_t const bitIdx = pos % 64; return (mWords[wordIdx] >> bitIdx) & 1; } //! \brief Set the bit value at a specific position. //! \param[in] pos The bit position (0 = LSB). //! \param[in] value The bit value to set. constexpr void setBit(uint64_t pos, bool value = true) noexcept { if (pos >= kBitCount) { return; } uint64_t const wordIdx = pos / 64; uint64_t const bitIdx = pos % 64; if (value) { mWords[wordIdx] |= (WordType{1} << bitIdx); } else { mWords[wordIdx] &= ~(WordType{1} << bitIdx); } } //! \brief Get the position of the highest set bit. //! \return The position (0-indexed), or -1 if zero. constexpr int32_t getHighestSetBit() const noexcept { for (int32_t i = kWordCount - 1; i >= 0; --i) { if (mWords[i] != 0) { // Count leading zeros portably (no compiler intrinsics). uint64_t w = mWords[i]; int32_t bit = 63; while (bit > 0 && (w & (uint64_t{1} << bit)) == 0) { --bit; } return i * 64 + bit; } } return -1; } // ======================================================================== // Comparison operators // ======================================================================== constexpr bool operator==(BigInt const& other) const noexcept { // Manual element-by-element comparison (std::array::operator== is not constexpr in C++17) for (uint64_t i = 0; i < kWordCount; ++i) { if (mWords[i] != other.mWords[i]) { return false; } } return true; } constexpr bool operator!=(BigInt const& other) const noexcept { return !(*this == other); } //! \brief Less-than comparison. //! Compares from most significant word down. constexpr bool operator<(BigInt const& other) const noexcept { for (int32_t i = kWordCount - 1; i >= 0; --i) { if (mWords[i] < other.mWords[i]) { return true; } if (mWords[i] > other.mWords[i]) { return false; } } return false; } constexpr bool operator<=(BigInt const& other) const noexcept { return !(other < *this); } constexpr bool operator>(BigInt const& other) const noexcept { return other < *this; } constexpr bool operator>=(BigInt const& other) const noexcept { return !(*this < other); } // ======================================================================== // Arithmetic operators // ======================================================================== //! \brief Add with overflow detection. //! \return Pair of (result, overflow_flag). static constexpr std::pair addWithOverflow(BigInt const& a, BigInt const& b) noexcept { BigInt result; uint64_t carry = 0; for (uint64_t i = 0; i < kWordCount; ++i) { // Add with carry using plain uint64_t. Overflow is detected by comparing // the result against the operand: if sum < a then overflow occurred. uint64_t sum = a.mWords[i] + b.mWords[i]; uint64_t c1 = (sum < a.mWords[i]) ? 1U : 0U; uint64_t sum2 = sum + carry; uint64_t c2 = (sum2 < sum) ? 1U : 0U; result.mWords[i] = sum2; carry = c1 + c2; } return {result, carry != 0}; } //! \brief Subtract with underflow detection. //! \return Pair of (result, underflow_flag). static constexpr std::pair subWithUnderflow(BigInt const& a, BigInt const& b) noexcept { BigInt result; uint64_t borrow = 0; for (uint64_t i = 0; i < kWordCount; ++i) { // Subtract with borrow using plain uint64_t. // Borrow is detected by: if a < b+borrow, then we borrowed from the next word. uint64_t sub = a.mWords[i] - b.mWords[i]; uint64_t b1 = (a.mWords[i] < b.mWords[i]) ? 1U : 0U; uint64_t sub2 = sub - borrow; uint64_t b2 = (sub < borrow) ? 1U : 0U; result.mWords[i] = sub2; borrow = b1 + b2; } return {result, borrow != 0}; } //! \brief Multiply with overflow detection. //! \return Pair of (result, overflow_flag). static std::pair multiplyWithOverflow(BigInt const& a, BigInt const& b) noexcept; //! \brief Divide with remainder. //! \param[in] dividend The dividend. //! \param[in] divisor The divisor. //! \return Pair of (quotient, remainder). //! \throws std::domain_error If divisor is zero. static std::pair divideWithRemainder(BigInt const& dividend, BigInt const& divisor); constexpr BigInt operator+(BigInt const& other) const noexcept { return addWithOverflow(*this, other).first; } constexpr BigInt& operator+=(BigInt const& other) noexcept { *this = *this + other; return *this; } constexpr BigInt operator-(BigInt const& other) const noexcept { return subWithUnderflow(*this, other).first; } constexpr BigInt& operator-=(BigInt const& other) noexcept { *this = *this - other; return *this; } BigInt operator*(BigInt const& other) const noexcept { return multiplyWithOverflow(*this, other).first; } BigInt operator/(BigInt const& other) const { return divideWithRemainder(*this, other).first; } BigInt operator%(BigInt const& other) const { return divideWithRemainder(*this, other).second; } // ======================================================================== // Shift operators (needed for division algorithm) // ======================================================================== constexpr BigInt operator<<(uint64_t shift) const noexcept { if (shift >= kBitCount) { return BigInt(); } if (shift == 0) { return *this; } BigInt result; uint64_t const wordShift = shift / 64; uint64_t const bitShift = shift % 64; if (bitShift == 0) { for (uint64_t i = wordShift; i < kWordCount; ++i) { result.mWords[i] = mWords[i - wordShift]; } } else { for (uint64_t i = wordShift; i < kWordCount; ++i) { result.mWords[i] = mWords[i - wordShift] << bitShift; if (i > wordShift) { result.mWords[i] |= mWords[i - wordShift - 1] >> (64 - bitShift); } } } return result; } constexpr BigInt& operator<<=(uint64_t shift) noexcept { *this = *this << shift; return *this; } // ======================================================================== // Increment/Decrement operators // ======================================================================== //! \brief Pre-increment operator. //! Handles carry propagation across words. constexpr BigInt& operator++() noexcept { for (uint64_t i = 0; i < kWordCount; ++i) { if (++mWords[i] != 0) { break; // No carry, done } // Carry propagates to next word } return *this; } constexpr BigInt operator++(int32_t) noexcept { BigInt tmp = *this; ++(*this); return tmp; } //! \brief Pre-decrement operator. //! Handles borrow propagation across words. constexpr BigInt& operator--() noexcept { for (uint64_t i = 0; i < kWordCount; ++i) { if (mWords[i]-- != 0) { break; // No borrow, done } // Borrow propagates to next word } return *this; } constexpr BigInt operator--(int32_t) noexcept { BigInt tmp = *this; --(*this); return tmp; } // ======================================================================== // String conversion // ======================================================================== //! \brief Convert to decimal string representation. //! \return The decimal string. std::string toString() const; //! \brief Get the lowest 64 bits as uint64_t. //! Useful when the value is known to fit in 64 bits. constexpr uint64_t toUint64() const noexcept { return mWords[0]; } private: std::array mWords{}; }; } // namespace sample #endif // TRT_SAMPLE_BIG_INT_H