(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》