// 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 #include #include #include #include #include // std::ostream, std::istream の前方宣言 #include // C++20 span for zero-copy operations #include // C++20 三方比較演算子 #include // std::pair #include // 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(value)) {} Int(uint8_t value) : Int(static_cast(value)) {} Int(short value) : Int(static_cast(value)) {} Int(unsigned short value) : Int(static_cast(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& 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 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 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(std::numeric_limits::max()); } else { return w <= (static_cast(std::numeric_limits::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(std::numeric_limits::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(std::numeric_limits::max()); } else { return w <= (static_cast(std::numeric_limits::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 words() const { return std::vector(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 && !std::is_same_v, int> = 0> Int& operator+=(T rhs) { if constexpr (std::is_signed_v) return plusEqScalar(static_cast(rhs)); else return plusEqScalar(static_cast(rhs)); } template && !std::is_same_v, int> = 0> Int& operator-=(T rhs) { if constexpr (std::is_signed_v) return minusEqScalar(static_cast(rhs)); else return minusEqScalar(static_cast(rhs)); } template && !std::is_same_v, int> = 0> Int& operator*=(T rhs) { if constexpr (std::is_signed_v) return mulEqScalar(static_cast(rhs)); else return mulEqScalar(static_cast(rhs)); } template && !std::is_same_v, int> = 0> Int& operator/=(T rhs) { if constexpr (std::is_signed_v) return divEqScalar(static_cast(rhs)); else return divEqScalar(static_cast(rhs)); } template && !std::is_same_v, int> = 0> Int& operator%=(T rhs) { if constexpr (std::is_signed_v) return modEqScalar(static_cast(rhs)); else return modEqScalar(static_cast(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 struct is_scalar_int : std::bool_constant< std::is_integral_v && !std::is_same_v> {}; template struct is_signed_scalar : std::bool_constant< is_scalar_int::value && std::is_signed_v> {}; template struct is_unsigned_scalar : std::bool_constant< is_scalar_int::value && std::is_unsigned_v> {}; } template::value, int> = 0> [[nodiscard]] Int operator+(const Int& lhs, T rhs) { if constexpr (std::is_signed_v) return addScalar(lhs, static_cast(rhs)); else return addScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator+(Int&& lhs, T rhs) { if constexpr (std::is_signed_v) return addScalar(std::move(lhs), static_cast(rhs)); else return addScalar(std::move(lhs), static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator+(T lhs, const Int& rhs) { if constexpr (std::is_signed_v) return addScalar(rhs, static_cast(lhs)); else return addScalar(rhs, static_cast(lhs)); } template::value, int> = 0> [[nodiscard]] Int operator-(const Int& lhs, T rhs) { if constexpr (std::is_signed_v) return subScalar(lhs, static_cast(rhs)); else return subScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator-(Int&& lhs, T rhs) { if constexpr (std::is_signed_v) return subScalar(std::move(lhs), static_cast(rhs)); else return subScalar(std::move(lhs), static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator-(T lhs, const Int& rhs) { if constexpr (std::is_signed_v) return rsubScalar(static_cast(lhs), rhs); else return rsubScalar(static_cast(lhs), rhs); } template::value, int> = 0> [[nodiscard]] Int operator*(const Int& lhs, T rhs) { if constexpr (std::is_signed_v) return mulScalar(lhs, static_cast(rhs)); else return mulScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator*(Int&& lhs, T rhs) { if constexpr (std::is_signed_v) return mulScalar(std::move(lhs), static_cast(rhs)); else return mulScalar(std::move(lhs), static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator*(T lhs, const Int& rhs) { if constexpr (std::is_signed_v) return mulScalar(rhs, static_cast(lhs)); else return mulScalar(rhs, static_cast(lhs)); } template::value, int> = 0> [[nodiscard]] Int operator/(const Int& lhs, T rhs) { if constexpr (std::is_signed_v) return divScalar(lhs, static_cast(rhs)); else return divScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator/(Int&& lhs, T rhs) { if constexpr (std::is_signed_v) return divScalar(std::move(lhs), static_cast(rhs)); else return divScalar(std::move(lhs), static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] uint64_t operator%(const Int& lhs, T rhs) { return modScalarU(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] Int operator%(const Int& lhs, T rhs) { return modScalar(lhs, static_cast(rhs)); } // 単一ワード比較演算子 template::value, int> = 0> [[nodiscard]] std::partial_ordering operator<=>(const Int& lhs, T rhs) { return cmpScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] bool operator==(const Int& lhs, T rhs) { return eqScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] std::partial_ordering operator<=>(const Int& lhs, T rhs) { return cmpScalar(lhs, static_cast(rhs)); } template::value, int> = 0> [[nodiscard]] bool operator==(const Int& lhs, T rhs) { return eqScalar(lhs, static_cast(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 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