1. 程式人生 > >【日記】Montgomery Modular Inverse

【日記】Montgomery Modular Inverse

(r, k) = AlmMonInv(a, m) = (a^-1)(2 ^k) (mod m)

/// <summary>
/// 返回{x, k};x = <paramref name="value"/>−1 * 2^k (mod <paramref name="modulo"/>) 
/// if gcd(<paramref name="value"/>, <paramref name="modulo"/>) = 1 and <paramref name="modulo"/> is odd
/// </summary>
private static uint[] AlmMonInv(uint value, uint modulo)
{
    if (value >= modulo)
        throw new ArgumentOutOfRangeException("value should be less than modulo");

    uint u = modulo, v = value, r = 0, s = 1, k = 0;
    while (v > 0)
    {
        if (IsEven(u))
        {
            u >>= 1; s <<= 1;
        }
        else if (IsEven(v))
        {
            v >>= 1; r <<= 1;
        }
        else if (u > v)
        {
            u = (u - v) >> 1; r += s; s <<= 1;
        }
        else if (v >= u)
        {
            v = (v - u) >> 1; s += r; r <<= 1;
        }
        ++k;
    }

    if (r >= modulo) r -= modulo;

    return new uint[] { modulo - r, k };
}

設 z = AlmMonInv(a, m) = (a^-1)(2^k) (mod m)

因為MonPro(x, y, m, m') = (xy) R ^-1 (mod m) = (xy)(2^32)^-1 (mod m)

所以ModInverse(a, m) = MonPro(z, 2^32-k, m, m') = (a^-1)(2^k)(2^32-k)(2^-32) (mod m) = a^-1 (mod m)

/// <summary>
/// <paramref name = "value" />−1 (mod<paramref name="modulo"/>) 
/// </summary>
public static int MonModInverse(int value, int modulo)
{
    if (modulo <= 0)
        throw new ArgumentOutOfRangeException("modulo should be positive");

    if (modulo == 1) return 0;

    var mod = Mod(value, modulo);
    if (mod == 1) return 1;

    var gcd = MonGCD(1U << 31, (uint)modulo);
    var result = AlmMonInv((uint)mod, (uint)modulo);
    if (result[1] > 32)
    {
        result[0] = MonPro(result[0], 1U, (uint)modulo, gcd[1]);
        result[1] -= 32;
    }
    result[0] = MonPro(result[0], 1U << (32 - (int)result[1]), (uint)modulo, gcd[1]);
            
    return (int)result[0];
}


/// <summary>
/// <paramref name="value"/> (mod <paramref name="modulo"/>)
/// </summary>
public static int Mod(int value, int modulo)
{
    if (modulo <= 0)
        throw new ArgumentOutOfRangeException("modulo should be positive");

    var remainder = value % modulo;
    return remainder < 0 ? remainder + modulo : remainder;
}

/// <summary>
/// returns {R, K},R*(2left) - K*right = 1.
/// left = 2 ^ n and right is odd
/// </summary>
public static uint[] MonGCD(uint left, uint right)
{
    var result = new uint[] { 1UL, 0UL };
    var s = left;
    var t = right;

    while (left > 0)
    {
        left >>= 1;
        result[1] >>= 1;

        if (IsEven(result[0]))
        {
            result[0] >>= 1;
        }
        else
        {
            result[0] = ((result[0] ^ t) >> 1) + (result[0] & t);//(result[0] + t) / 2
            result[1] += s;
        }
    }

    return result;
}


private static bool IsEven(uint n) => (n & 1) == 0;

注意:a^-1 (mod m) 只有在gcm(a, m) == 1時存在,本方法並沒有檢查a和m是否互素。

MonModPro 參考《Montgomery Moduluar Exponentiation