singrdk/base/Libraries/Crypto/Digits.cs

886 lines
37 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Diagnostics;
namespace Microsoft.Singularity.Crypto.PublicKey {
class Digits {
readonly Digit[] _digits;
readonly int _digitI;
internal Digits(int n) { _digits = new Digit[n]; _digitI = 0; }
internal Digits(Digit[] digits) { _digits = digits; _digitI = 0; }
Digits(Digit[] digits, int digitI) {
_digits = digits;
_digitI = digitI;
}
internal Digit this[int i] {
get {
if (_digitI + i < _digits.Length) {
return _digits[_digitI + i];
} else {
return 0;
}
}
set {
if (_digitI + i < _digits.Length) {
_digits[_digitI + i] = value;
} else {
Debug.Assert(value == 0, "internal error");
}
}
}
int _N { get { return _digits.Length - _digitI; } }
internal void _Set(Digits from, int n) {
for (int i = 0; i < n; i++) { this[i] = from[i]; }
}
internal void _SetAll(Digit val, int n) {
for (int i = 0; i < n; i++) { this[i] = val; }
}
internal bool _Overlaps(Digits that) {
return _digits.Equals(that._digits);
}
public static Digits operator +(Digits digits, int i) {
return new Digits(digits._digits, digits._digitI + i);
}
public override string ToString() {
string s = "(";
for (int i = _digitI; i < _N; i++) {
if (i > _digitI) { s += ","; }
s += _digits[i].ToString();
}
return s + ")";
}
internal static void Set(Digits a, int aN, Digits b, int bN) {
b._Set(a, aN);
(b + aN)._SetAll(0, bN - aN);
}
internal static Digit GetBit(Digits a, int i) {
return a[i / Digit.BitN] >> i % Digit.BitN & 1;
}
internal static void SetBit(Digits a, int i, uint bit) {
int j = i / Digit.BitN, k = i % Digit.BitN;
a[j] = a[j] & ~((1U & ~bit) << k) | (1U & bit) << k;
}
internal static int Compare(Digits a, Digit ivalue, int aN) {
return Compare(a, aN, new Digits(new Digit[] { ivalue }), 1);
}
internal static int SigDigitN(Digits a, int aN) {
int i = aN;
while (i != 0 && a[i - 1] == 0) { i--; }
return i;
}
internal static int SigBitN(Digits a, int aN) {
int sigDigitN = SigDigitN(a, aN);
if (sigDigitN == 0) {
Debug.Assert(false, "untested code");
return 0;
}
return (int)((sigDigitN - 1) * Digit.BitN
+ Digit.SigBitN(a[sigDigitN - 1]));
}
internal static Digit
Accumulate(Digits a, Digit mult, Digits b, int n) {
Digit carry = 0;
for (int i = 0; i != n; i++) {
UInt64 dtemp = (UInt64)mult * a[i] + b[i] + carry;
b[i] = Digit2.Lo(dtemp);
carry = Digit2.Hi(dtemp);
}
return carry;
}
internal static Digit
Decumulate(Digits a, Digit mult, Digits b, int n) {
Digit borrow = 0;
for (int i = 0; i != n; i++) {
UInt64 dtemp;
unchecked {
dtemp = (UInt64)b[i] - borrow - (UInt64)mult * a[i];
}
b[i] = Digit2.Lo(dtemp);
unchecked { borrow = 0 - Digit2.Hi(dtemp); }
}
return borrow;
}
internal static void Shift(Digits a, int shiftBitN, Digits b, int n) {
int itranslate = shiftBitN >= 0
? shiftBitN / Digit.BitN
: -(-shiftBitN / Digit.BitN);
ShiftLost(a, shiftBitN - Digit.BitN * itranslate, b, n);
if (itranslate < 0) {
Debug.Assert(false, "untested code");
int dtranslate = -itranslate;
for (int i = 0; i != n; i++) {
Debug.Assert(false, "untested code");
b[i] = i + dtranslate < n ? b[i + dtranslate] : 0;
}
} else if (itranslate > 0) {
Debug.Assert(false, "untested code");
int dtranslate = itranslate;
for (int i = n; i-- != 0; ) {
Debug.Assert(false, "untested code");
b[i] = i >= dtranslate ? b[i - dtranslate] : 0;
}
}
}
internal static Digit
ShiftLost(Digits a, int bitScale, Digits b, int n) {
if (n == 0) { Debug.Assert(false, "untested code"); return 0; }
if (bitScale == 0) {
Debug.Assert(false, "untested code");
b._Set(a, n);
return 0;
}
Digit bitNLost = 0;
if (bitScale > 0) {
if (bitScale > Digit.BitN) { throw new ArgumentException(); }
if (bitScale == Digit.BitN) {
bitNLost = a[n - 1];
for (int i = n - 1; i != 0; i--) { b[i] = a[i - 1]; }
b[0] = 0;
} else {
for (int i = 0; i != n; i++) {
Digit bNew = a[i] << bitScale | bitNLost;
bitNLost = a[i] >> Digit.BitN - bitScale;
b[i] = bNew;
}
}
} else {
if (bitScale < -Digit.BitN) { throw new ArgumentException(); }
if (bitScale == -Digit.BitN) {
bitNLost = a[0];
for (int i = 1; i != n; i++) { b[i - 1] = a[i]; }
b[n - 1] = 0;
} else {
for (int i = n; i-- != 0; ) {
Digit bNew = a[i] >> -bitScale | bitNLost;
bitNLost = a[i] << Digit.BitN + bitScale;
b[i] = bNew;
}
bitNLost >>= Digit.BitN + bitScale;
}
}
return bitNLost;
}
internal static void
Mul(Digits a, int aN, Digits b, int bN, Digits c) {
Debug.Assert(!c._Overlaps(a) && !c._Overlaps(b)
, "overlapping arguments");
Digits p1, p2;
int i, n1, n2;
if (aN > bN) {
p1 = a;
p2 = b;
n1 = aN;
n2 = bN;
} else {
p2 = a;
p1 = b;
n2 = aN;
n1 = bN;
}
if (n2 == 0) {
Debug.Assert(false, "untested code");
c._SetAll(0, n1);
return;
}
c[n1] = Mul(p1, p2[0], c, n1);
for (i = 1; i != n2; i++) {
c[i + n1] = Accumulate(p1, p2[i], c + i, n1);
}
}
internal static Digit Mul(Digits a, Digit mult, Digits b, int n) {
Digit carry = 0;
for (int i = 0; i != n; i++) {
UInt64 dtemp = (UInt64)mult * a[i] + carry;
b[i] = Digit2.Lo(dtemp);
carry = Digit2.Hi(dtemp);
}
return carry;
}
internal static void
BytesToDigits(byte[] bytes, int bytesI, Digits digits, int bitN) {
if (bitN == 0) { Debug.Assert(false, "untested code"); return; }
int digitN = (bitN + (Digit.BitN - 1)) / Digit.BitN;
digits._SetAll(0, digitN);
for (int iDigit = 0; iDigit != digitN; iDigit++) {
int byteNLeft = (bitN + 7) / 8 - 4 * iDigit;
Digit digit = 0;
for (
int iByte = 0;
iByte != (4 > byteNLeft ? byteNLeft : 4);
iByte++
) {
digit
^= (Digit)bytes[bytesI + byteNLeft - 1 - iByte] << 8 * iByte;
}
digits[iDigit] = digit;
}
digits[digitN - 1] &= Digit.MaxValue >> Digit.BitN * digitN - bitN;
}
internal static void
DigitsToBytes(Digits digits, byte[] bytes, int byteI, int bitN) {
for (int i = 0; i != (bitN + (Digit.BitN - 1)) / Digit.BitN; i++) {
Digit dvalue = digits[i];
int byteNLeft = (bitN + 7) / 8 - 4 * i;
for (int j = 0; j != (byteNLeft > 4 ? 4 : byteNLeft); j++) {
bytes[byteI + byteNLeft - 1 - j] = (byte)(dvalue & 0xff);
dvalue >>= 8;
}
}
}
internal static void Div(
Digits num
, int numN
, Digits denom
, int denomN
, Reciprocal recip
, Digits q
, Digits r
) {
if (denomN == 0) { throw new DivideByZeroException(); }
if (num == null || denom == null || r == null) {
throw new ArgumentNullException();
}
Debug.Assert(!r._Overlaps(num) && !r._Overlaps(denom)
, "overlapping arguments");
if (q != null) {
Debug.Assert(
!q._Overlaps(num) && !q._Overlaps(denom) && !r._Overlaps(q)
, "overlapping arguments"
);
}
Digit dlead = denom[denomN - 1];
if (dlead == 0) { throw new ArgumentException(); }
if (numN < denomN) {
Debug.Assert(false, "untested code");
Set(num, numN, r, denomN);
return;
}
if (denomN == 1) { Div(num, dlead, recip, q, numN, r); return; }
if (recip == null) {
recip = new Reciprocal();
DivPrecondition(denom, denomN, recip);
}
r[denomN - 1] = 0;
r._Set(num + (numN - denomN + 1), denomN - 1);
for (int iq = numN - denomN + 1; iq-- != 0; ) {
Digit rTop = r[denomN - 1];
for (int i = denomN - 1; i != 0; i--) { r[i] = r[i - 1]; }
r[0] = num[iq];
Digit qest;
if (rTop == 0 && Compare(r, denom, denomN) < 0) {
qest = 0;
} else {
qest
= recip.EstQuotient(rTop, r[denomN - 1], r[denomN - 2]);
if (qest < Digit.MaxValue) { qest += 1; }
Digit borrow = Decumulate(denom, qest, r, denomN);
if (borrow > rTop) {
qest -= 1;
borrow -= Add(r, denom, r, denomN);
}
Debug.Assert(borrow == rTop, "internal error");
}
if (q != null) { q[iq] = qest; }
}
}
static void Div(
Digits numer
, Digit den
, Reciprocal recip
, Digits q
, int n
, Digits r
) {
Digit carry = 0;
int nLeft = n;
if (nLeft > 0 && numer[nLeft - 1] < den) {
nLeft--;
carry = numer[nLeft];
if (q != null) { q[nLeft] = 0; }
}
if (recip == null && nLeft < 2) {
for (int i = nLeft; i-- != 0; ) {
Digit qest = 0;
Digit2.Div((UInt64)carry << Digit.BitN | numer[i]
, den
, out qest
, out carry);
if (q != null) { q[i] = qest; }
}
} else {
if (recip == null) {
recip = new Reciprocal();
DivPrecondition(new Digits(new Digit[] { den }), 1, recip);
}
for (int i = nLeft; i-- != 0; ) {
Digit qest = 0;
Digit2.Div((UInt64)carry << Digit.BitN | numer[i]
, den
, recip
, out qest
, out carry);
if (q != null) { q[i] = qest; }
}
}
r[0] = carry;
}
internal static void
DivPrecondition(Digits denom, int denomN, Reciprocal recip) {
if (denom == null) { throw new ArgumentNullException(); }
if (denomN == 0 || denom[denomN - 1] == 0) {
throw new ArgumentException();
}
int recipBitShift = Digit.BitN - Digit.SigBitN(denom[denomN - 1]);
Digit dlead2 = denom[denomN - 1]
, dlead1 = denomN >= 2 ? denom[denomN - 2] : 0
, dlead0 = denomN >= 3 ? denom[denomN - 3] : 0
, dShiftHi = dlead2 << recipBitShift
| dlead1 >> 1 >> Digit.BitN - 1 - recipBitShift
, dShiftLo = dlead1 << recipBitShift
| dlead0 >> 1 >> Digit.BitN - 1 - recipBitShift;
Digit recipMpy, r;
Digit2.Div((UInt64)(Digit.MaxValue - dShiftHi) << Digit.BitN
| Digit.MaxValue - dShiftLo
, dShiftHi
, out recipMpy
, out r);
if (Digit2.Hi((UInt64)recipMpy * dShiftLo) > r) { recipMpy -= 1; }
r = (Digit.MaxValue >> recipBitShift) - denom[denomN - 1];
for (int id = denomN; id-- != 0 && r < recipMpy; ) {
UInt64 test1 = (UInt64)r << Digit.BitN
| Digit.MaxValue - (id > 0 ? denom[id - 1] : 0)
, test2 = (UInt64)recipMpy * denom[id];
if (test2 > test1) { recipMpy -= 1; break; }
test1 = test1 - test2;
r = Digit2.Lo(test1);
if (Digit2.Hi(test1) != 0) { break; }
}
recip._shiftBitN = recipBitShift;
recip._multiplier = recipMpy;
}
static uint Add(Digits a, int aN, Digits b, int bN, Digits c) {
if (aN < bN || aN < 0 || bN < 0) { throw new ArgumentException(); }
uint carry = Add(a, b, c, bN);
return Add(a + bN, carry, c + bN, aN - bN);
}
static void
AddFull(Digits a, int aN, Digits b, int bN, Digits c, out int cN) {
uint carry;
if (aN < bN) {
Debug.Assert(false, "untested code");
carry = Add(b, bN, a, aN, c);
cN = bN;
} else {
carry = Add(a, aN, b, bN, c);
cN = aN;
}
if (carry != 0) {
Debug.Assert(false, "untested code");
c[cN++] = carry;
}
}
internal static uint Add(Digits a, Digit immediate, Digits b, int n) {
uint carry = immediate;
for (int i = 0; i != n; i++) {
Digit bi = a[i] + carry;
b[i] = bi;
if (bi >= carry) {
if (a != b) { (b + i + 1)._Set(a + i + 1, n - i - 1); }
return 0;
}
carry = 1;
}
return carry;
}
internal static uint Add(Digits a, Digits b, Digits c, int n) {
uint carry = 0;
for (int i = 0; i != n; i++) {
Digit ai = a[i], bi = b[i], sum = unchecked(carry + (ai + bi));
c[i] = sum;
carry = ((ai | bi) ^ (ai ^ bi) & sum) >> Digit.BitN - 1;
}
return carry;
}
internal static int
AddSub(Digits a, Digits b, Digits c, Digits d, int n) {
uint carry1 = 0, carry2 = 0;
for (int i = 0; i != n; i++) {
Digit ai = a[i]
, bi = b[i]
, ci = c[i]
, sum1 = unchecked(ai + bi + carry1)
, sum2 = unchecked(sum1 - ci - carry2);
d[i] = sum2;
carry1 = (sum1 ^ (sum1 ^ ai | sum1 ^ bi)) >> Digit.BitN - 1;
carry2 = (sum1 ^ (sum1 ^ ci | sum1 ^ sum2)) >> Digit.BitN - 1;
}
return (int)carry1 - (int)carry2;
}
internal static int Compare(Digits a, int aN, Digits b, int bN) {
int la = aN, lb = bN;
while (la > lb) { if (a[la - 1] != 0) { return +1; } la--; }
while (lb > la) { if (b[lb - 1] != 0) { return -1; } lb--; }
Debug.Assert(la == lb, "internal error");
while (la != 0) {
if (a[la - 1] != b[la - 1]) {
return a[la - 1] > b[la - 1] ? +1 : -1;
}
la--;
}
return 0;
}
internal static int Compare(Digits a, Digits b, int n) {
for (int i = n; i-- != 0; ) {
if (a[i] != b[i]) { return a[i] > b[i] ? +1 : -1; }
}
return 0;
}
internal static int CompareSum(Digits a, Digits b, Digits c, int n) {
int sumPrev = 0;
for (int i = n; i-- != 0; ) {
Digit aval = a[i]
, bval = b[i]
, cval = c[i]
, sumNow = unchecked(aval + bval);
Debug.Assert(sumPrev == 0 || sumPrev == -1, "internal error");
sumPrev += (sumNow < aval ? 1 : 0) - (sumNow < cval ? 1 : 0);
unchecked { sumNow -= cval; }
if (
sumPrev != unchecked((int)(uint)sumNow)
|| unchecked(sumPrev + 3 & 2) == 0
) {
return (sumPrev + 2 & 2) - 1;
}
}
return sumPrev;
}
internal static int Sub(Digits a, Digit isub, Digits b, int n) {
Digit borrow = isub;
for (int i = 0; i != n; i++) {
Digit ai = a[i];
b[i] = ai - borrow;
if (ai >= borrow) {
if (a != b) { (b + i + 1)._Set(a + i + 1, n - i - 1); }
return 0;
}
borrow = 1;
}
return (int)borrow;
}
internal static uint Sub(Digits a, Digits b, Digits c, int n) {
uint borrow = 0;
for (int i = 0; i != n; i++) {
Digit ai = a[i], bi = b[i], sum = unchecked(ai - bi - borrow);
c[i] = sum;
borrow = (ai ^ (ai ^ bi | ai ^ sum)) >> Digit.BitN - 1;
}
return borrow;
}
internal static void ExtendedGcd(
Digits a
, int aN
, Digits b
, int bN
, Digits ainvmodb
, Digits binvmoda
, Digits gcd
, out int lgcd
) {
Debug.Assert(!gcd._Overlaps(a)
&& !gcd._Overlaps(b)
&& !ainvmodb._Overlaps(a)
&& !ainvmodb._Overlaps(b)
&& !ainvmodb._Overlaps(gcd)
, "overlapping arguments");
int aSigDigitN = SigDigitN(a, aN), bSigDigitN = SigDigitN(b, bN);
if (aSigDigitN == 0 || bSigDigitN == 0) {
throw new ArgumentException();
}
int[] abN = new int[2] { aSigDigitN, bSigDigitN };
if (a == null || b == null || gcd == null || ainvmodb == null) {
throw new ArgumentNullException();
}
int maxDigitN = aN > bN ? aN : bN;
Digits ab0Padded = new Digits(maxDigitN + 2)
, ab1Padded = new Digits(maxDigitN + 2)
, tempProd = new Digits(2 * maxDigitN)
, tempQ = new Digits(maxDigitN + 1);
Digits[] ab = new Digits[] { ab0Padded + 1, ab1Padded + 1 };
ab[0]._Set(a, aSigDigitN);
ab[1]._Set(b, bSigDigitN);
Digits[]
tempsMul
= new Digits[] { new Digits(maxDigitN), new Digits(maxDigitN) };
tempsMul[0][0] = 1;
int mulN = 1, iterations = 0;
while (abN[0] != 0 && abN[1] != 0) {
int abN0 = abN[0], abN1 = abN[1];
Digits pab0top = ab[0] + (abN0 - 1)
, pab1top = ab[1] + (abN1 - 1);
Digit topword0 = pab0top[0], topword1 = pab1top[0];
int topsigbitN0 = Digit.SigBitN(topword0)
, topsigbitN1 = Digit.SigBitN(topword1)
, sigbitN0 = Digit.BitN * (abN0 - 1) + topsigbitN0
, sigbitN1 = Digit.BitN * (abN1 - 1) + topsigbitN1
, maxSigBitN = sigbitN0 > sigbitN1 ? sigbitN0 : sigbitN1
, maxlab = abN0 > abN1 ? abN0 : abN1
, ibig = Compare(ab[1], abN1, ab[0], abN0) > 0 ? 1 : 0
, ismall = 1 - ibig;
Digit[] mat22 = new Digit[4];
iterations++;
Debug.Assert(iterations <= Digit.BitN * (aN + bN + 1)
, "too many iterations");
if (maxlab == 1) {
Digit ab0, ab1, m00, m01, m10, m11;
if (topword0 > Digit.MaxValue - topword1) {
if (ibig == 0) {
topword0 -= topword1;
} else {
topword1 -= topword0;
}
Digit carry = Add(tempsMul[0]
, tempsMul[1]
, tempsMul[ibig]
, mulN);
if (carry != 0) {
Debug.Assert(false, "untested code");
Debug.Assert(mulN < bN, "internal error");
tempsMul[ibig][mulN] = carry;
mulN++;
}
}
ab0 = topword0;
ab1 = topword1;
m00 = 1;
m01 = 0;
m10 = 0;
m11 = 1;
while (ab1 != 0) {
if (ab0 >= ab1) {
if (ab0 >> 2 >= ab1) {
Digit q = ab0 / ab1;
ab0 -= q * ab1;
m00 += q * m10;
m01 += q * m11;
} else {
do {
ab0 -= ab1;
m00 += m10;
m01 += m11;
} while (ab0 >= ab1);
}
}
Debug.Assert(ab1 > ab0, "internal error");
if (ab0 == 0) { break; }
if (ab1 >> 2 >= ab0) {
Digit q = ab1 / ab0;
ab1 -= q * ab0;
m10 += q * m00;
m11 += q * m01;
} else {
do {
ab1 -= ab0;
m10 += m00;
m11 += m01;
} while (ab1 >= ab0);
}
Debug.Assert(ab0 > ab1, "internal error");
}
ab[0][0] = ab0;
ab[1][0] = ab1;
abN[0] = ab0 != 0 ? 1 : 0;
abN[1] = ab1 != 0 ? 1 : 0;
mat22[0] = m00;
mat22[1] = m01;
mat22[2] = m10;
mat22[3] = m11;
Digits carrys = new Digits(2);
Mul22U(mat22, tempsMul[0], tempsMul[1], mulN, carrys);
if (carrys[0] == 0 && carrys[1] == 0) {
} else {
Debug.Assert(mulN < bN, "internal error");
tempsMul[0][mulN] = carrys[0];
tempsMul[1][mulN] = carrys[1];
mulN++;
}
} else if (
sigbitN0 > sigbitN1 + Digit.BitN / 2
|| sigbitN1 > sigbitN0 + Digit.BitN / 2
) {
int smallMulN = SigDigitN(tempsMul[ismall], mulN);
Div(ab[ibig]
, abN[ibig]
, ab[ismall]
, abN[ismall]
, null
, tempQ
, tempProd);
int lQ = SigDigitN(tempQ, abN[ibig] - abN[ismall] + 1);
abN[ibig] = SigDigitN(tempProd, abN[ismall]);
ab[ibig]._Set(tempProd, abN[ibig]);
Mul(tempQ, lQ, tempsMul[ismall], smallMulN, tempProd);
AddFull(tempProd
, SigDigitN(tempProd, lQ + smallMulN)
, tempsMul[ibig]
, mulN
, tempsMul[ibig]
, out mulN);
} else {
int norm = (int)(Digit.BitN * maxlab - maxSigBitN);
pab0top[1] = pab1top[1] = 0;
Debug.Assert(maxlab >= 2, "internal error");
UInt64 lead0
= (UInt64)
(ab[0][maxlab - 1] << norm
| ab[0][maxlab - 2] >> 1 >> Digit.BitN - 1 - norm)
<< Digit.BitN
| (ab[0][maxlab - 2] << norm
| ab[0][maxlab - 3] >> 1 >> Digit.BitN - 1 - norm)
, lead1
= (UInt64)
(ab[1][maxlab - 1] << norm
| ab[1][maxlab - 2] >> 1 >> Digit.BitN - 1 - norm)
<< Digit.BitN
| (ab[1][maxlab - 2] << norm
| ab[1][maxlab - 3] >> 1 >> Digit.BitN - 1 - norm);
LehmerMat22(lead0, lead1, mat22);
if ((mat22[1] | mat22[2]) == 0) {
Debug.Assert(false, "untested code");
Debug.Assert(
mat22[0] == 1 && mat22[3] == 1, "internal error");
mat22[ibig + 1] = 1;
}
int lab = abN0 > abN1 ? abN0 : abN1;
int[] scarrys = new int[2];
Mul22S(mat22, ab[0], ab[1], lab, scarrys);
Debug.Assert(scarrys[0] == 0 && scarrys[1] == 0
, "internal error");
int abN0Sig = lab, abN1Sig = lab;
while (abN0Sig != 0 && ab[0][abN0Sig - 1] == 0) {
abN0Sig--;
}
while (abN1Sig != 0 && ab[1][abN1Sig - 1] == 0) {
abN1Sig--;
}
abN[0] = abN0Sig;
abN[1] = abN1Sig;
Digits carrys = new Digits(2);
Mul22U(mat22, tempsMul[0], tempsMul[1], mulN, carrys);
if (carrys[0] == 0 && carrys[1] == 0) {
} else {
Debug.Assert(mulN < bN, "internal error");
tempsMul[0][mulN] = carrys[0];
tempsMul[1][mulN] = carrys[1];
mulN++;
}
}
}
Digit igcd = (Digit)(abN[0] == 0 ? 1U : 0U);
lgcd = abN[igcd];
gcd._Set(ab[igcd], lgcd);
Debug.Assert(Compare(b, tempsMul[1 - igcd], bN) >= 0
&& Compare(tempsMul[1 - igcd], tempsMul[igcd], bN) >= 0
, "internal error");
if (igcd == 0) {
ainvmodb._Set(tempsMul[0], bN);
} else {
Sub(tempsMul[0], tempsMul[1], ainvmodb, bN);
}
if (binvmoda != null) {
Sub(tempsMul[1 - igcd], ainvmodb, tempsMul[1 - igcd], bN);
Mul(a, aN, tempsMul[1 - igcd], bN, tempProd);
Add(tempProd, aN + bN, gcd, lgcd, tempProd);
Div(tempProd
, aN + bSigDigitN
, b
, bSigDigitN
, null
, tempQ
, tempsMul[1 - igcd]);
Debug.Assert(SigDigitN(tempsMul[1 - igcd], bSigDigitN) == 0
, "internal error");
binvmoda._Set(tempQ, aN);
}
}
static void
LehmerMat22(UInt64 lead0orig, UInt64 lead1orig, Digit[] mat22) {
Digit lead0h = Digit2.Hi(lead0orig)
, lead0l = Digit2.Lo(lead0orig)
, lead1h = Digit2.Hi(lead1orig)
, lead1l = Digit2.Lo(lead1orig);
bool progress = true;
Digit m00 = 1, m01 = 0, m10 = 0, m11 = 1;
Debug.Assert(lead0h != 0
&& lead1h != 0
&& ((lead0h | lead1h) & 1U << Digit.BitN - 1) != 0
, "internal error");
while (progress) {
progress = false;
if (lead0h - 1 > lead1h && lead0h != 0) {
if (lead0h >> 2 >= lead1h + 2) {
Digit q = lead0h / (lead1h + 2);
UInt64 prod10By11 = (UInt64)q * (m10 + m11)
, prod1l = (UInt64)q * lead1l;
Debug.Assert(q > 3, "internal error");
if (
Digit2.Hi(prod10By11) == 0
&& Digit2.Lo(prod10By11)
<= Digit.MaxValue / 2 - m00 - m01
) {
Digit prod10 = q * m10;
progress = true;
lead0h
-= (Digit)((long)(UInt32)q * lead1h
+ Digit2.Hi(prod1l)
+ (Digit2.Lo(prod1l) > lead0l ? 1L : 0L));
unchecked { lead0l -= Digit2.Lo(prod1l); }
m00 += prod10;
m01 += Digit2.Lo(prod10By11) - prod10;
Debug.Assert((m00 | m01) < 1U << Digit.BitN - 1
, "internal error");
}
} else {
Digit overflowTest;
do {
m00 += m10;
m01 += m11;
overflowTest = (m00 | m01) & 1U << Digit.BitN - 1;
lead0h -= lead1h + (lead1l > lead0l ? 1U : 0U);
unchecked { lead0l -= lead1l; }
} while (overflowTest == 0 && lead0h >= lead1h + 2);
progress = true;
if (overflowTest != 0) {
progress = false;
m00 -= m10;
m01 -= m11;
}
}
}
if (lead1h - 1 > lead0h && lead1h != 0) {
if (lead1h >> 2 >= lead0h + 2) {
Digit q = lead1h / (lead0h + 2);
UInt64 prod00And01 = (UInt64)q * (m00 + m01)
, prod0l = (UInt64)q * lead0l;
Debug.Assert(q > 3, "internal error");
if (
Digit2.Hi(prod00And01) == 0
&& Digit2.Lo(prod00And01)
<= Digit.MaxValue / 2 - m10 - m11
) {
Digit prod00 = q * m00;
progress = true;
lead1h -= q * lead0h
+ Digit2.Hi(prod0l)
+ (Digit2.Lo(prod0l) > lead1l ? 1U : 0U);
unchecked { lead1l -= Digit2.Lo(prod0l); }
m10 += prod00;
m11 += Digit2.Lo(prod00And01) - prod00;
Debug.Assert((m10 | m11) < 1U << Digit.BitN - 1
, "internal error");
}
} else {
Digit overflowTest;
do {
m10 += m00;
m11 += m01;
overflowTest = (m10 | m11) & 1U << Digit.BitN - 1;
lead1h -= lead0h + (lead0l > lead1l ? 1U : 0U);
unchecked { lead1l -= lead0l; }
} while (overflowTest == 0 && lead1h >= lead0h + 2);
progress = true;
if (overflowTest != 0) {
progress = false;
m10 -= m00;
m11 -= m01;
}
}
}
}
Debug.Assert(((m00 | m01 | m10 | m11) & 1U << Digit.BitN - 1) == 0
, "internal error");
mat22[0] = m00;
mat22[1] = m01;
mat22[2] = m10;
mat22[3] = m11;
}
static void Mul22U(
Digit[] mat
, Digits vec1
, Digits vec2
, int lvec
, Digits carrys
) {
Digit carry1 = 0, carry2 = 0;
Digit m11 = mat[0], m12 = mat[1], m21 = mat[2], m22 = mat[3];
if (m12 > Digit.MaxValue - m11 || m21 > Digit.MaxValue - m22) {
throw new ArgumentException();
}
for (int i = 0; i != lvec; i++) {
UInt64 prod11 = (UInt64)m11 * vec1[i] + carry1
, prod21 = (UInt64)m21 * vec1[i] + carry2
, prod12 = (UInt64)m12 * vec2[i] + Digit2.Lo(prod11)
, prod22 = (UInt64)m22 * vec2[i] + Digit2.Lo(prod21);
vec1[i] = Digit2.Lo(prod12);
vec2[i] = Digit2.Lo(prod22);
carry1 = Digit2.Hi(prod11) + Digit2.Hi(prod12);
carry2 = Digit2.Hi(prod21) + Digit2.Hi(prod22);
}
carrys[0] = carry1;
carrys[1] = carry2;
}
static void
Mul22S(Digit[] mat, Digits vec1, Digits vec2, int lvec, int[] carrys) {
int carry1 = 0, carry2 = 0;
Digit m11 = mat[0], m12 = mat[1], m21 = mat[2], m22 = mat[3];
if (((m11 | m12 | m21 | m22) & 1U << Digit.BitN - 1) != 0) {
throw new ArgumentException();
}
for (int i = 0; i != lvec; i++) {
UInt64 prod11 = (UInt64)m11 * vec1[i]
, prod12 = (UInt64)m12 * vec2[i]
, prod21 = (UInt64)m21 * vec1[i]
, prod22 = (UInt64)m22 * vec2[i]
, prod1 = unchecked(prod11 + (UInt64)carry1)
, prod2 = unchecked(prod22 + (UInt64)carry2);
prod1 = unchecked(prod1 - prod12);
prod2 = unchecked(prod2 - prod21);
vec1[i] = Digit2.Lo(prod1);
vec2[i] = Digit2.Lo(prod2);
carry1 = unchecked((int)Digit2.Hi(prod1));
carry2 = unchecked((int)Digit2.Hi(prod2));
}
carrys[0] = carry1;
carrys[1] = carry2;
}
internal static void
Random(Digits digits, int digitN, Random generator) {
for (int i = 0; i < digitN; i++) {
digits[i] = Digit.Random(generator);
}
}
}
public class Reciprocal {
internal Digit _multiplier;
internal int _shiftBitN;
internal Digit EstQuotient(Digit n2, Digit n1, Digit n0) {
Digit nShiftHi
= n2 << _shiftBitN | n1 >> 1 >> Digit.BitN - 1 - _shiftBitN
, nShiftLo
= n1 << _shiftBitN | n0 >> 1 >> Digit.BitN - 1 - _shiftBitN;
UInt64 qprod = ((UInt64)nShiftHi << Digit.BitN | nShiftLo)
+ (UInt64)nShiftHi * _multiplier;
if ((nShiftLo & 1U << Digit.BitN - 1) != 0) {
qprod += _multiplier >> 1;
}
return Digit2.Hi(qprod);
}
}
}