// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // IntModular.hpp // Modular arithmetic (modular exponentiation, multiplicative inverse) for Int #ifndef SANGI_INT_MODULAR_HPP #define SANGI_INT_MODULAR_HPP #include "IntBase.hpp" namespace sangi { /** * @brief Modular arithmetic (mod, modular exponentiation, multiplicative inverse) * * Provides the basic modular-arithmetic operations used in RSA, elliptic-curve * cryptography, the discrete-log problem, and so on. * - powerMod: modular exponentiation (binary exponentiation, O(log exp)) * - inverseMod: multiplicative inverse (uses extended Euclidean algorithm) */ class SANGI_API IntModular { public: /** * @brief Normalized modulo (always satisfies 0 <= r < m) * * Unlike C++'s operator%, this behaves like Mathematica's Mod[]. * It returns a non-negative remainder even for negative dividends. * * Examples: * mod(-7, 5) = 3 (C++'s -7 % 5 is -2) * mod(7, 5) = 2 * mod(-7, -5) = -2 * * @param x Dividend * @param m Modulus * @return Remainder r where 0 <= r < |m| * * Special cases: * mod(x, 0) = NaN (division by zero) * mod(NaN, m) = NaN * mod(infinity, m) = NaN */ static Int mod(const Int& x, const Int& m); /** * @brief Modular exponentiation: base^exp mod m * * Uses a binary exponentiation (right-to-left) algorithm. * Complexity: O(log exp) multiplications. * * Examples: * powerMod(2, 10, 1000) = 1024 mod 1000 = 24 * powerMod(3, 100, 7) = 3^100 mod 7 = 4 * * Negative exponents: * powerMod(a, -n, m) = inverseMod(a^n, m) * i.e. a^(-n) == (a^n)^(-1) mod m * * @param base Base * @param exp Exponent (may be negative) * @param m Modulus * @return base^exp mod m * * Special cases: * powerMod(0, 0, m) = 1 (definition of 0^0) * powerMod(x, 0, m) = 1 * powerMod(x, n, 1) = 0 * powerMod(NaN, n, m) = NaN * powerMod(x, n, 0) = NaN (division by zero) * * Constraints: * m must satisfy m > 0 * When exp < 0, gcd(base, m) must equal 1 (condition for the inverse to exist) */ static Int powerMod(const Int& base, const Int& exp, const Int& m); /** * @brief Multiplicative inverse: a^(-1) mod m * * Returns x satisfying a * x == 1 (mod m). * Computed via the extended Euclidean algorithm. * * Examples: * inverseMod(3, 7) = 5 (because 3*5 = 15 == 1 mod 7) * inverseMod(2, 5) = 3 (because 2*3 = 6 == 1 mod 5) * * Optimization: when m is prime * a^(-1) == a^(m-2) mod m (Fermat's little theorem) * Specifying is_prime=true enables this faster path. * * @param a Integer whose inverse is sought * @param m Modulus * @param is_prime true when it is known that m is prime (for optimization, default false) * @return a^(-1) mod m * * Special cases: * When the inverse does not exist (gcd(a, m) != 1): returns Int::Zero() * inverseMod(0, m) = 0 (no inverse) * inverseMod(1, m) = 1 * inverseMod(NaN, m) = NaN * inverseMod(a, 0) = NaN (division by zero) * inverseMod(a, 1) = 0 (every a is == 0 mod 1) * * Constraints: * m must satisfy m > 1 (with m=1, everything is congruent to 0) * gcd(a, m) must equal 1 (a and m must be coprime) */ static Int inverseMod(const Int& a, const Int& m, bool is_prime = false); /** * @brief Constant-time modular exponentiation: base^exp mod m * (equivalent to GMP's mpz_powm_sec) * * Intended for cryptographic use. Eliminates branches and memory-access * patterns that depend on the bits of the exponent. * - Fixed window width (not sliding window) * - Reads every table entry each time and selects conditionally (cmov-style) * - Performs sqr+mul regardless of whether the bit is 0/1, discarding the dummy result * * @param base Base (non-negative) * @param exp Exponent (non-negative) * @param m Modulus (odd positive integer) * @return base^exp mod m * * Constraints: * exp >= 0, m > 0, m must be odd * (Falls back to powerMod for even m) */ static Int powerModSec(const Int& base, const Int& exp, const Int& m); }; } // namespace sangi #endif // SANGI_INT_MODULAR_HPP