// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// IntBase.hpp
// 多倍長整数クラスの基本定義

#ifndef CALX_INT_BASE_HPP
#define CALX_INT_BASE_HPP

#include "../../basic_types.hpp"
#include "../../numeric_state.hpp"  // NumericStateTraitsの定義を含む
#include "UInt128.hpp"
#include "SboWords.hpp"
#include <vector>
#include <string>
#include <string_view>
#include <cstdint>
#include <limits>
#include <iosfwd>   // std::ostream, std::istream の前方宣言
#include <span>     // C++20 span for zero-copy operations
#include <compare>  // C++20 三方比較演算子
#include <utility>      // std::pair
#include <type_traits>  // std::is_integral_v, std::enable_if_t

namespace calx {

    // 前方宣言
    class IntOps;
    class IntSpecialStates;
    class IntIOUtils;

    /**
     * @brief 多倍長整数クラス
     *
     * 任意精度の整数を表現するクラスです。内部では64ビットワードの
     * 可変長配列として値を保持します。
     */
    class Int {
    public:
        // ワード数の上限なし — メモリの許す限り任意精度
        // (SboWords は size_t ベースのため型制約もなし)

        // コンストラクタ
        Int();
        Int(int8_t value)  : Int(static_cast<int>(value)) {}
        Int(uint8_t value) : Int(static_cast<int>(value)) {}
        Int(short value)   : Int(static_cast<int>(value)) {}
        Int(unsigned short value) : Int(static_cast<int>(value)) {}
        Int(int value);
        Int(uint64_t value);
        Int(int64_t value);
        // 文字列からのコンストラクタ
        explicit Int(std::string_view str, int base = 10);
        Int(const Int& other);
        Int(Int&& other) noexcept;

        // デストラクタ
        ~Int() = default;

        // 代入演算子
        Int& operator=(const Int& other);
        Int& operator=(Int&& other) noexcept;
        Int& operator=(int value);

        // 特殊値の生成
        [[nodiscard]] static Int Zero() { return Int(0); }
        [[nodiscard]] static Int One() { return Int(1); }
        [[nodiscard]] static Int NaN();
        [[nodiscard]] static Int PositiveInfinity();
        [[nodiscard]] static Int NegativeInfinity();
        [[nodiscard]] static Int fromState(NumericState state, NumericError error = NumericError::None) {
            Int result;

            // 状態に応じて適切な初期化
            switch (state) {
            case NumericState::Normal:
                // 通常の値（ゼロで初期化）
                result = Int(0);
                break;

            case NumericState::NaN:
                // NaN状態
                result.m_state = NumericState::NaN;
                result.m_error = error;
                result.m_sign = 0;  // NaNの場合、符号は意味を持たない
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::PositiveInfinity:
                // 正の無限大
                result.m_state = NumericState::PositiveInfinity;
                result.m_error = error;
                result.m_sign = 1;  // 正の符号
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::NegativeInfinity:
                // 負の無限大
                result.m_state = NumericState::NegativeInfinity;
                result.m_error = error;
                result.m_sign = -1;  // 負の符号
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::ComplexInfinity:
                // 複素無限大
                result.m_state = NumericState::ComplexInfinity;
                result.m_error = error;
                result.m_sign = 0;  // 符号は意味を持たない
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::Overflow:
                // オーバーフロー
                result.m_state = NumericState::Overflow;
                result.m_error = error;
                result.m_sign = 1;  // 通常、オーバーフローは正の方向
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::Underflow:
                // アンダーフロー（整数では稀だが、状態の完全性のため対応）
                result.m_state = NumericState::Underflow;
                result.m_error = error;
                result.m_sign = 1;  // 通常、アンダーフローは正の方向
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::PositiveZero:
                // 正のゼロ（多倍長整数では通常は区別しないが、状態の完全性のため対応）
                result.m_state = NumericState::PositiveZero;
                result.m_error = error;
                result.m_sign = 0;  // ゼロの符号は0
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::NegativeZero:
                // 負のゼロ（多倍長整数では通常は区別しないが、状態の完全性のため対応）
                result.m_state = NumericState::NegativeZero;
                result.m_error = error;
                result.m_sign = 0;  // ゼロの符号は0
                result.m_words.clear();  // ワード配列をクリア
                break;

            case NumericState::Divergent:
            case NumericState::Oscillating:
            case NumericState::SlowConvergence:
            case NumericState::NotConverged:
            case NumericState::TruncatedConvergence:
                // 収束に関する状態
                result.m_state = state;
                result.m_error = error;
                result.m_sign = 0;  // 符号は意味を持たない
                result.m_words.clear();  // ワード配列をクリア
                break;

            default:
                // その他の特殊状態
                result.m_state = state;
                result.m_error = error;
                result.m_sign = 0;  // デフォルトでは符号なし
                result.m_words.clear();  // ワード配列をクリア
                break;
            }

            return result;
        }
        [[nodiscard]] static Int fromRawWords(const std::vector<uint64_t>& words, int sign) {
            // 新しいIntオブジェクトを作成
            Int result;

            // 空の配列の場合は0を返す
            if (words.empty()) {
                result.m_words.clear();
                result.m_sign = 0;  // 0の符号は0
                result.m_state = NumericState::Normal;
                result.m_error = NumericError::None;
                return result;
            }

            // ワード配列をコピー
            result.m_words.assign(words.begin(), words.end());

            // 符号を設定（入力の妥当性をチェック）
            if (sign > 0) {
                result.m_sign = 1;  // 正の値
            }
            else if (sign < 0) {
                result.m_sign = -1; // 負の値
            }
            else {
                result.m_sign = 0;  // 0（ワード配列が空でなくても符号が0なら0と解釈）
                result.m_words.clear();  // 0なのでワード配列をクリア
            }

            // 状態を正常に設定
            result.m_state = NumericState::Normal;
            result.m_error = NumericError::None;

            // 上位の0を削除して正規化
            result.normalize();

            return result;
        }

        // span ベースの fromRawWords (zero-copy 最適化)
        [[nodiscard]] static Int fromRawWords(std::span<const uint64_t> words, int sign) {
            // 新しいIntオブジェクトを作成
            Int result;

            // 空の配列の場合は0を返す
            if (words.empty()) {
                result.m_words.clear();
                result.m_sign = 0;
                result.m_state = NumericState::Normal;
                result.m_error = NumericError::None;
                return result;
            }

            // span から vector にコピー (最終的には必要)
            result.m_words.assign(words.begin(), words.end());

            // 符号を設定
            if (sign > 0) {
                result.m_sign = 1;
            }
            else if (sign < 0) {
                result.m_sign = -1;
            }
            else {
                result.m_sign = 0;
                result.m_words.clear();
            }

            // 状態を正常に設定
            result.m_state = NumericState::Normal;
            result.m_error = NumericError::None;

            // 正規化
            result.normalize();

            return result;
        }

        // MSB 正規化済みデータ用の高速版 (normalize() スキップ)
        // 前提: words の MSB ワードが非ゼロ (先頭ゼロなし)
        [[nodiscard]] static Int fromRawWordsPreNormalized(std::span<const uint64_t> words, int sign) {
            Int result;
            if (words.empty()) {
                result.m_words.clear();
                result.m_sign = 0;
                result.m_state = NumericState::Normal;
                result.m_error = NumericError::None;
                return result;
            }
            result.m_words.assign(words.begin(), words.end());
            result.m_sign = (sign > 0) ? 1 : (sign < 0) ? -1 : 0;
            if (result.m_sign == 0) result.m_words.clear();
            result.m_state = NumericState::Normal;
            result.m_error = NumericError::None;
            return result;
        }

        // 特殊値のファクトリメソッド (camelCase 版)
        [[nodiscard]] static Int nan();
        [[nodiscard]] static Int infinity();
        [[nodiscard]] static Int negInfinity();

        // 状態確認メソッド
        [[nodiscard]] constexpr bool isZero() const { return m_state == NumericState::Normal && m_words.empty(); }
        [[nodiscard]] constexpr bool isOne() const {
            // 特殊状態の場合はfalse
            if (m_state != NumericState::Normal) {
                return false;
            }

            // 符号が正でなければfalse
            if (m_sign <= 0) {
                return false;
            }

            // ワード数が1つでない場合はfalse
            if (m_words.size() != 1) {
                return false;
            }

            // 唯一のワードの値が1であればtrue
            return m_words[0] == 1;
        }
        [[nodiscard]] constexpr bool isPositive() const { return m_sign > 0 && !isZero(); }
        [[nodiscard]] constexpr bool isNegative() const { return m_sign < 0; }
        [[nodiscard]] constexpr bool isNaN() const { return m_state == NumericState::NaN; }
        [[nodiscard]] constexpr bool isInfinite() const {
            return m_state == NumericState::PositiveInfinity ||
                m_state == NumericState::NegativeInfinity;
        }
        [[nodiscard]] constexpr bool isSpecialState() const { return m_state != NumericState::Normal; }
        [[nodiscard]] constexpr int getSign() const { return m_sign; }
        [[nodiscard]] constexpr NumericState getState() const { return m_state; }
        [[nodiscard]] constexpr NumericError getError() const { return m_error; }
        [[nodiscard]] constexpr bool isNormal() const { return m_state == NumericState::Normal; }
        [[nodiscard]] constexpr bool isDivergent() const { return NumericStateTraits::isDivergent(m_state); }
        [[nodiscard]] constexpr DivergenceDetail getDivergenceDetail() const { return DivergenceDetail::None; }

        // 偶数・奇数判定
        [[nodiscard]] bool isEven() const {
            // 特殊状態の場合は false
            if (isSpecialState()) return false;
            // ゼロは偶数
            if (isZero()) return true;
            // 最下位ビットが 0 なら偶数
            return !getBit(0);
        }

        [[nodiscard]] bool isOdd() const {
            // 特殊状態の場合は false
            if (isSpecialState()) return false;
            // ゼロは偶数（奇数ではない）
            if (isZero()) return false;
            // 最下位ビットが 1 なら奇数
            return getBit(0);
        }

        // 型範囲判定
        [[nodiscard]] constexpr bool fitsByte() const {
            if (isSpecialState()) return false;
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;
            uint64_t w = m_words[0];
            if (m_sign >= 0) {
                return w <= 127;
            } else {
                return w <= 128;
            }
        }

        [[nodiscard]] constexpr bool fitsUByte() const {
            if (isSpecialState()) return false;
            if (m_sign < 0) return false;
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;
            return m_words[0] <= 255;
        }

        [[nodiscard]] constexpr bool fitsShort() const {
            if (isSpecialState()) return false;
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;
            uint64_t w = m_words[0];
            if (m_sign >= 0) {
                return w <= 32767;
            } else {
                return w <= 32768;
            }
        }

        [[nodiscard]] constexpr bool fitsUShort() const {
            if (isSpecialState()) return false;
            if (m_sign < 0) return false;
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;
            return m_words[0] <= 65535;
        }

        [[nodiscard]] constexpr bool fitsInt() const {
            // 特殊状態は収まらない
            if (isSpecialState()) return false;
            // int の範囲: [-2^31, 2^31-1]
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;  // ゼロ
            uint64_t w = m_words[0];
            if (m_sign >= 0) {
                return w <= static_cast<uint64_t>(std::numeric_limits<int>::max());
            } else {
                return w <= (static_cast<uint64_t>(std::numeric_limits<int>::max()) + 1);
            }
        }

        [[nodiscard]] constexpr bool fitsUInt() const {
            if (isSpecialState()) return false;
            if (m_sign < 0) return false;
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;
            return m_words[0] <= static_cast<uint64_t>(std::numeric_limits<unsigned int>::max());
        }

        [[nodiscard]] constexpr bool fitsInt64() const {
            // 特殊状態は収まらない
            if (isSpecialState()) return false;
            // int64_t の範囲: [-2^63, 2^63-1]
            if (m_words.size() > 1) return false;
            if (m_words.empty()) return true;  // ゼロ
            uint64_t w = m_words[0];
            if (m_sign >= 0) {
                return w <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
            } else {
                return w <= (static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1);
            }
        }

        [[nodiscard]] constexpr bool fitsUInt64() const {
            // 特殊状態は収まらない
            if (isSpecialState()) return false;
            // 負の数は uint64_t に収まらない
            if (m_sign < 0) return false;
            // uint64_t の範囲: [0, 2^64-1]
            if (m_words.size() > 1) return false;
            // 1ワード以下なら必ず収まる
            return true;
        }

        // 指定された基数での桁数を返す
        [[nodiscard]] size_t digitCount(int base = 10) const;

        // 指定された基数での桁数を高速推定 (GMP mpz_sizeinbase 互換)
        // 2のべき乗基数では正確、それ以外は正確または1大きい値を返す
        [[nodiscard]] size_t sizeInBase(int base = 10) const;

        // 整数 log2 (= bitLength - 1)。0以下は 0 を返す
        [[nodiscard]] size_t ilog2() const;

        // 整数 log10 (= digitCount(10) - 1)。0以下は 0 を返す
        [[nodiscard]] size_t ilog10() const;

        // 割り切れるかの判定
        [[nodiscard]] bool isDivisible(const Int& divisor) const;

        // 合同判定
        [[nodiscard]] bool isCongruent(const Int& other, const Int& modulus) const;

        // 2つの Int 値を入れ替え
        void swap(Int& other) noexcept {
            m_words.swap(other.m_words);
            std::swap(m_sign, other.m_sign);
            std::swap(m_state, other.m_state);
            std::swap(m_error, other.m_error);
        }

        // ワードアクセス用メソッド
        [[nodiscard]] constexpr uint64_t word(size_t index) const { return (index < m_words.size()) ? m_words[index] : 0; }
        [[nodiscard]] constexpr size_t size() const { return m_words.size(); }
        [[nodiscard]] std::vector<uint64_t> words() const { return std::vector<uint64_t>(m_words.begin(), m_words.end()); }
        [[nodiscard]] const uint64_t* data() const noexcept { return m_words.data(); }

        // 両端のゼロワードを in-place 削除。下位側の削除ワード数を返す。
        // Float::normalize() で指数調整に使用。
        size_t trimZeroWords();

        // 数値変換
        [[nodiscard]] int toInt() const;
        [[nodiscard]] int64_t toInt64() const;
        [[nodiscard]] uint64_t toUInt64() const;
        [[nodiscard]] size_t toSizeT() const;
        [[nodiscard]] double toDouble() const;
        // double × 2^exp 分解 (GMP mpz_get_d_2exp 相当)
        // 戻り値 d ∈ [0.5, 1.0), *exp は 2 の指数。|this| = d × 2^(*exp)
        // ゼロの場合 d=0.0, *exp=0
        [[nodiscard]] double toDouble2Exp(int64_t* exp) const;
        [[nodiscard]] std::string toString(int base = 10) const;

        // 文字列関連メソッド
        [[nodiscard]] std::string toHexString(bool uppercase = false) const;
        [[nodiscard]] std::string toBinaryString() const;
        [[nodiscard]] std::string toOctalString() const;
        [[nodiscard]] static Int fromString(std::string_view str, int base = 10);

        // ビット操作用メソッド
        [[nodiscard]] bool getBit(size_t position) const;
        void setBit(size_t position);
        void clearBit(size_t position);
        void complementBit(size_t position);
        [[nodiscard]] size_t bitLength() const;
        [[nodiscard]] size_t countLeadingZeros() const;
        [[nodiscard]] size_t countTrailingZeros() const;

        // 2^b で割り切れるか (GMP mpz_divisible_2exp_p 相当)
        // 下位 b ビットが全 0 なら true。b==0 は常に true。
        [[nodiscard]] bool isDivisibleBy2Exp(size_t b) const;

        // (this - c) が 2^b で割り切れるか (GMP mpz_congruent_2exp_p 相当)
        // 下位 b ビットが this と c で一致すれば true。
        [[nodiscard]] bool isCongruent2Exp(const Int& c, size_t b) const;

        // セットビット数 (population count, GMP mpz_popcount 相当)
        // 非負整数: 立っている 1 ビットの数を返す
        // 負整数: 2の補数表現では無限個の 1 があるため SIZE_MAX を返す
        // (GMP 互換: 負の場合 ULONG_MAX)
        [[nodiscard]] size_t popcount() const;

        // 最初のセットビット/クリアビットの位置を検索
        // GMP: mpz_scan0, mpz_scan1
        [[nodiscard]] size_t scanBit0(size_t startPos = 0) const;
        [[nodiscard]] size_t scanBit1(size_t startPos = 0) const;

        // ハミング距離 (異なるビット位置の数)
        // GMP: mpz_hamdist
        [[nodiscard]] static size_t hammingDistance(const Int& a, const Int& b);

        // 単項演算子（メンバー関数）
        [[nodiscard]] Int operator-() const& {
            if (m_sign == 0) [[likely]] return *this;
            if (m_state == NumericState::Normal) [[likely]] {
                Int result = *this;
                result.m_sign = -m_sign;
                return result;
            }
            return negate_slow();
        }
        [[nodiscard]] Int operator-() && {
            if (m_sign == 0) [[likely]] return std::move(*this);
            if (m_state == NumericState::Normal) [[likely]] {
                m_sign = -m_sign;
                return std::move(*this);
            }
            return negate_slow();
        }
        [[nodiscard]] Int operator+() const { return *this; }

        // 複合代入演算子（メンバー関数として）
        Int& operator+=(const Int& other);
        Int& operator-=(const Int& other);
        Int& operator*=(const Int& other);
        Int& operator/=(const Int& other);
        Int& operator%=(const Int& other);

        // 単一ワード複合代入 (GMP _ui/_si 相当: Int 一時オブジェクト構築を回避)
        // テンプレートで全整数型を受け付け、内部で uint64_t/int64_t に分岐
        template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
        Int& operator+=(T rhs) {
            if constexpr (std::is_signed_v<T>) return plusEqScalar(static_cast<int64_t>(rhs));
            else return plusEqScalar(static_cast<uint64_t>(rhs));
        }
        template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
        Int& operator-=(T rhs) {
            if constexpr (std::is_signed_v<T>) return minusEqScalar(static_cast<int64_t>(rhs));
            else return minusEqScalar(static_cast<uint64_t>(rhs));
        }
        template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
        Int& operator*=(T rhs) {
            if constexpr (std::is_signed_v<T>) return mulEqScalar(static_cast<int64_t>(rhs));
            else return mulEqScalar(static_cast<uint64_t>(rhs));
        }
        template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
        Int& operator/=(T rhs) {
            if constexpr (std::is_signed_v<T>) return divEqScalar(static_cast<int64_t>(rhs));
            else return divEqScalar(static_cast<uint64_t>(rhs));
        }
        template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
        Int& operator%=(T rhs) {
            if constexpr (std::is_signed_v<T>) return modEqScalar(static_cast<int64_t>(rhs));
            else return modEqScalar(static_cast<uint64_t>(rhs));
        }

    private:
        [[nodiscard]] Int negate_slow() const;  // operator-() の NaN/Infinity パス
        Int& plusEqScalar(int64_t rhs);
        Int& plusEqScalar(uint64_t rhs);
        Int& minusEqScalar(int64_t rhs);
        Int& minusEqScalar(uint64_t rhs);
        Int& mulEqScalar(int64_t rhs);
        Int& mulEqScalar(uint64_t rhs);
        Int& divEqScalar(int64_t rhs);
        Int& divEqScalar(uint64_t rhs);
        Int& modEqScalar(int64_t rhs);
        Int& modEqScalar(uint64_t rhs);
    public:

        // ビット演算子（メンバー関数として）
        Int& operator&=(const Int& other);
        Int& operator|=(const Int& other);
        Int& operator<<=(int shift);
        Int& operator>>=(int shift);

        // べき乗代入演算子 (a ^= b  ⟺  a = pow(a, b))
        Int& operator^=(const Int& other);

        // 状態管理メソッド
        /// 符号を in-place で反転する (O(1), コピーなし)。
        /// `-a` (operator-) はコピーを伴うため、結果を変数に戻す場合は
        /// `a.negate()` の方が高速。
        void negate() {
            if (m_state == NumericState::Normal) [[likely]] {
                m_sign = -m_sign;
            }
        }

        void setSign(int sign) {
            m_sign = sign;
            // Normal 状態でゼロ値（m_words が空）の場合、符号は必ず 0
            if (sign != 0 && m_state == NumericState::Normal && m_words.empty()) [[unlikely]] {
                m_sign = 0;
            }
        }
        void setState(NumericState state, NumericError error = NumericError::None);

        // 比較演算子のフレンド宣言 (C++20 三方比較)
        friend std::partial_ordering operator<=>(const Int& lhs, const Int& rhs);
        friend bool operator==(const Int& lhs, const Int& rhs);

        // ビット演算子のフレンド宣言
        friend Int operator~(const Int& value);
        friend Int operator~(Int&& value);

        // ストリーム演算子のフレンド宣言
        friend std::ostream& operator<<(std::ostream& os, const Int& value);
        friend std::istream& operator>>(std::istream& is, Int& value);

        // ユーティリティ関数のフレンド宣言
        friend class IntOps;
        friend class IntSpecialStates;
        friend class IntIOUtils;
        friend class Float;
        friend class FloatOps;
        friend class IntDivision;
        friend class IntMultiplication;
        friend class IntRandom;
        friend class IntSqrt;

        friend Int abs(const Int& value);
        friend Int abs(Int&& value);

    private:
        // 内部状態
        SboWords m_words;  // 値を表す64ビットワードの配列 (SBO: 4 words inline)
        int m_sign;                    // 符号
        NumericState m_state;           // 数値状態
        NumericError m_error;           // エラー情報

        // 絶対値の比較ヘルパー関数
        static int compareAbsoluteValues(const Int& lhs, const Int& rhs);

        // 内部状態を正規化するメソッド
        void normalize();

        // 特殊状態の設定
        void setNaN(NumericError error = NumericError::None);
    };

    //------------------------------------------------------------------------------
    // グローバル演算子の宣言
    //------------------------------------------------------------------------------

    // 算術演算子
    [[nodiscard]] Int operator+(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator+(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int operator+(const Int& lhs, Int&& rhs);
    [[nodiscard]] Int operator+(Int&& lhs, Int&& rhs);
    [[nodiscard]] Int operator-(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator-(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int operator-(const Int& lhs, Int&& rhs);
    [[nodiscard]] Int operator-(Int&& lhs, Int&& rhs);
    [[nodiscard]] Int operator*(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator/(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator/(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int operator%(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator%(Int&& lhs, const Int& rhs);

    // 単一ワード算術演算子 — 実装関数 (IntOperators.cpp)
    // ユーザーコードからはテンプレート版を使用
    [[nodiscard]] Int addScalar(const Int& lhs, uint64_t rhs);
    [[nodiscard]] Int addScalar(Int&& lhs, uint64_t rhs);
    [[nodiscard]] Int addScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] Int addScalar(Int&& lhs, int64_t rhs);
    [[nodiscard]] Int subScalar(const Int& lhs, uint64_t rhs);
    [[nodiscard]] Int subScalar(Int&& lhs, uint64_t rhs);
    [[nodiscard]] Int subScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] Int subScalar(Int&& lhs, int64_t rhs);
    [[nodiscard]] Int rsubScalar(uint64_t lhs, const Int& rhs);
    [[nodiscard]] Int rsubScalar(int64_t lhs, const Int& rhs);
    [[nodiscard]] Int mulScalar(const Int& lhs, uint64_t rhs);
    [[nodiscard]] Int mulScalar(Int&& lhs, uint64_t rhs);
    [[nodiscard]] Int mulScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] Int mulScalar(Int&& lhs, int64_t rhs);
    [[nodiscard]] Int divScalar(const Int& lhs, uint64_t rhs);
    [[nodiscard]] Int divScalar(Int&& lhs, uint64_t rhs);
    [[nodiscard]] Int divScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] Int divScalar(Int&& lhs, int64_t rhs);
    [[nodiscard]] uint64_t modScalarU(const Int& lhs, uint64_t rhs);
    [[nodiscard]] Int modScalar(const Int& lhs, int64_t rhs);

    // 単一ワード比較演算子 — 実装関数
    [[nodiscard]] std::partial_ordering cmpScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] bool eqScalar(const Int& lhs, int64_t rhs);
    [[nodiscard]] std::partial_ordering cmpScalar(const Int& lhs, uint64_t rhs);
    [[nodiscard]] bool eqScalar(const Int& lhs, uint64_t rhs);

    // テンプレート演算子: 全整数型を受け付けオーバーロード曖昧性を回避
    namespace detail {
        template<typename T> struct is_scalar_int : std::bool_constant<
            std::is_integral_v<T> && !std::is_same_v<T, bool>> {};
        template<typename T> struct is_signed_scalar : std::bool_constant<
            is_scalar_int<T>::value && std::is_signed_v<T>> {};
        template<typename T> struct is_unsigned_scalar : std::bool_constant<
            is_scalar_int<T>::value && std::is_unsigned_v<T>> {};
    }

    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator+(const Int& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return addScalar(lhs, static_cast<int64_t>(rhs));
        else return addScalar(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator+(Int&& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return addScalar(std::move(lhs), static_cast<int64_t>(rhs));
        else return addScalar(std::move(lhs), static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator+(T lhs, const Int& rhs) {
        if constexpr (std::is_signed_v<T>) return addScalar(rhs, static_cast<int64_t>(lhs));
        else return addScalar(rhs, static_cast<uint64_t>(lhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator-(const Int& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return subScalar(lhs, static_cast<int64_t>(rhs));
        else return subScalar(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator-(Int&& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return subScalar(std::move(lhs), static_cast<int64_t>(rhs));
        else return subScalar(std::move(lhs), static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator-(T lhs, const Int& rhs) {
        if constexpr (std::is_signed_v<T>) return rsubScalar(static_cast<int64_t>(lhs), rhs);
        else return rsubScalar(static_cast<uint64_t>(lhs), rhs);
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator*(const Int& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return mulScalar(lhs, static_cast<int64_t>(rhs));
        else return mulScalar(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator*(Int&& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return mulScalar(std::move(lhs), static_cast<int64_t>(rhs));
        else return mulScalar(std::move(lhs), static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator*(T lhs, const Int& rhs) {
        if constexpr (std::is_signed_v<T>) return mulScalar(rhs, static_cast<int64_t>(lhs));
        else return mulScalar(rhs, static_cast<uint64_t>(lhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator/(const Int& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return divScalar(lhs, static_cast<int64_t>(rhs));
        else return divScalar(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_scalar_int<T>::value, int> = 0>
    [[nodiscard]] Int operator/(Int&& lhs, T rhs) {
        if constexpr (std::is_signed_v<T>) return divScalar(std::move(lhs), static_cast<int64_t>(rhs));
        else return divScalar(std::move(lhs), static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_unsigned_scalar<T>::value, int> = 0>
    [[nodiscard]] uint64_t operator%(const Int& lhs, T rhs) {
        return modScalarU(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_signed_scalar<T>::value, int> = 0>
    [[nodiscard]] Int operator%(const Int& lhs, T rhs) {
        return modScalar(lhs, static_cast<int64_t>(rhs));
    }

    // 単一ワード比較演算子
    template<typename T, std::enable_if_t<detail::is_signed_scalar<T>::value, int> = 0>
    [[nodiscard]] std::partial_ordering operator<=>(const Int& lhs, T rhs) {
        return cmpScalar(lhs, static_cast<int64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_signed_scalar<T>::value, int> = 0>
    [[nodiscard]] bool operator==(const Int& lhs, T rhs) {
        return eqScalar(lhs, static_cast<int64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_unsigned_scalar<T>::value, int> = 0>
    [[nodiscard]] std::partial_ordering operator<=>(const Int& lhs, T rhs) {
        return cmpScalar(lhs, static_cast<uint64_t>(rhs));
    }
    template<typename T, std::enable_if_t<detail::is_unsigned_scalar<T>::value, int> = 0>
    [[nodiscard]] bool operator==(const Int& lhs, T rhs) {
        return eqScalar(lhs, static_cast<uint64_t>(rhs));
    }

    // インクリメント/デクリメント演算子
    Int& operator++(Int& value);
    [[nodiscard]] Int operator++(Int& value, int);
    Int& operator--(Int& value);
    [[nodiscard]] Int operator--(Int& value, int);

    // ビット演算子
    [[nodiscard]] Int operator&(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator&(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int operator&(const Int& lhs, Int&& rhs);
    [[nodiscard]] Int operator&(Int&& lhs, Int&& rhs);
    [[nodiscard]] Int operator|(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int operator|(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int operator|(const Int& lhs, Int&& rhs);
    [[nodiscard]] Int operator|(Int&& lhs, Int&& rhs);
    [[nodiscard]] Int operator~(const Int& value);
    [[nodiscard]] Int operator~(Int&& value);
    [[nodiscard]] Int operator<<(const Int& lhs, int shift);
    [[nodiscard]] Int operator<<(Int&& lhs, int shift);
    [[nodiscard]] Int operator>>(const Int& lhs, int shift);
    [[nodiscard]] Int operator>>(Int&& lhs, int shift);

    // べき乗演算子 (数式記法: a ^ b = pow(a, b))
    [[nodiscard]] Int operator^(const Int& lhs, const Int& rhs);

    // ビット XOR (関数版)
    [[nodiscard]] Int bitwiseXor(const Int& lhs, const Int& rhs);
    [[nodiscard]] Int bitwiseXor(Int&& lhs, const Int& rhs);
    [[nodiscard]] Int bitwiseXor(const Int& lhs, Int&& rhs);
    [[nodiscard]] Int bitwiseXor(Int&& lhs, Int&& rhs);

    // 比較演算子 (C++20 三方比較)
    std::partial_ordering operator<=>(const Int& lhs, const Int& rhs);
    bool operator==(const Int& lhs, const Int& rhs);

    // ストリーム演算子
    std::ostream& operator<<(std::ostream& os, const Int& value);
    std::istream& operator>>(std::istream& is, Int& value);

    // ユーティリティ関数
    [[nodiscard]] Int abs(const Int& value);
    [[nodiscard]] Int abs(Int&& value);
    [[nodiscard]] Int gcd(const Int& a, const Int& b);
    [[nodiscard]] Int lcm(const Int& a, const Int& b);
    [[nodiscard]] Int pow(const Int& base, unsigned int exponent);
    [[nodiscard]] Int pow(const Int& base, const Int& exponent);
    [[nodiscard]] Int powMod(const Int& base, const Int& exponent, const Int& modulus);
    [[nodiscard]] std::size_t bitCount(const Int& value);

    // 因子除去 (GMP mpz_remove 互換)
    // value から factor を全て除去し {除去後の値, 回数} を返す
    [[nodiscard]] std::pair<Int, uint64_t> removeFactor(const Int& value, const Int& factor);

    // swap 関数（ADL のため名前空間内に定義）
    inline void swap(Int& a, Int& b) noexcept {
        a.swap(b);
    }
    [[nodiscard]] bool isProbablePrime(const Int& value, int iterations = 25);

} // namespace calx

#endif // CALX_INT_BASE_HPP