private static void MpDiv
(
uint[] rgulU, // In | U
int ciulU, // In | # of digits in U
uint[] rgulD, // In | D
int ciulD, // In | # of digits in D
uint[] rgulQ, // Out | Q
out int ciulQ, // Out | # of digits in Q
uint[] rgulR, // Out | R
out int ciulR // Out | # of digits in R
)
{
Debug.Assert(ciulU > 0, "ciulU > 0", "In method MpDiv");
Debug.Assert(ciulD > 0, "ciulD > 0", "In method MpDiv");
Debug.Assert(rgulU.Length == s_cNumeMax);
Debug.Assert(rgulD.Length == s_cNumeMax);
// Division by zero?
if (ciulD == 1 && rgulD[0] == 0)
{
ciulQ = ciulR = 0;
}
// Check for simplest case, so it'll be fast
else if (ciulU == 1 && ciulD == 1)
{
MpSet(rgulQ, out ciulQ, rgulU[0] / rgulD[0]);
MpSet(rgulR, out ciulR, rgulU[0] % rgulD[0]);
}
// If D > U then do not divide at all
else if (ciulD > ciulU)
{
MpMove(rgulU, ciulU, rgulR, out ciulR); // R = U
MpSet(rgulQ, out ciulQ, 0); // Q = 0
}
// Try to divide faster - check for remaining good sizes (8 / 4, 8 / 8)
else if (ciulU <= 2)
{
ulong dwlU, dwlD, dwlT;
dwlU = DWL(rgulU[0], rgulU[1]);
dwlD = rgulD[0];
if (ciulD > 1)
dwlD += (((ulong)rgulD[1]) << 32);
dwlT = dwlU / dwlD;
rgulQ[0] = LO(dwlT);
rgulQ[1] = HI(dwlT);
ciulQ = (HI(dwlT) != 0) ? 2 : 1;
dwlT = dwlU % dwlD;
rgulR[0] = LO(dwlT);
rgulR[1] = HI(dwlT);
ciulR = (HI(dwlT) != 0) ? 2 : 1;
}
// If we are dividing by one digit - use simpler routine
else if (ciulD == 1)
{
MpMove(rgulU, ciulU, rgulQ, out ciulQ); // Q = U
uint remainder;
MpDiv1(rgulQ, ref ciulQ, rgulD[0], out remainder); // Q = Q / D, R = Q % D
rgulR[0] = remainder;
ciulR = 1;
}
// Worst case. Knuth, "The Art of Computer Programming", 3rd edition, vol.II, Alg.D, pg 272
else
{
ciulQ = ciulR = 0;
uint D1, ulDHigh, ulDSecond;
int iulRindex;
if (rgulU != rgulR)
MpMove(rgulU, ciulU, rgulR, out ciulR); // R = U
ciulQ = ciulU - ciulD + 1;
ulDHigh = rgulD[ciulD - 1];
// D1. Normalize so high digit of D >= BASE/2 - that guarantee
// that QH will not be too far from the correct digit later in D3
rgulR[ciulU] = 0;
iulRindex = ciulU;
D1 = (uint)(s_ulInt32Base / ((ulong)ulDHigh + 1));
if (D1 > 1)
{
MpMul1(rgulD, ref ciulD, D1);
ulDHigh = rgulD[ciulD - 1];
MpMul1(rgulR, ref ciulR, D1);
}
ulDSecond = rgulD[ciulD - 2];
// D2 already done - iulRindex initialized before normalization of R.
// D3-D7. Loop on iulRindex - obtaining digits one-by-one, as "in paper"
do
{
uint QH, RH;
int iulDindex, iulRwork;
ulong dwlAccum, dwlMulAccum;
// D3. Calculate Q hat - estimation of the next digit
dwlAccum = DWL(rgulR[iulRindex - 1], rgulR[iulRindex]);
if (ulDHigh == rgulR[iulRindex])
QH = (uint)(s_ulInt32Base - 1);
else
QH = (uint)(dwlAccum / ulDHigh);
ulong ulTemp = QH;
RH = (uint)(dwlAccum - ulTemp * ulDHigh);
while (ulDSecond * ulTemp > DWL(rgulR[iulRindex - 2], RH))
{
QH--;
if (RH >= (uint)-((int)ulDHigh))
break;
RH += ulDHigh;
ulTemp = QH;
}
// D4. Multiply and subtract: (some digits of) R -= D * QH
for (dwlAccum = s_ulInt32Base, dwlMulAccum = 0, iulDindex = 0, iulRwork = iulRindex - ciulD;
iulDindex < ciulD; iulDindex++, iulRwork++)
{
ulong ulTemp2 = rgulD[iulDindex];
dwlMulAccum += QH * ulTemp2;
dwlAccum += (ulong)rgulR[iulRwork] - LO(dwlMulAccum);
dwlMulAccum = HI(dwlMulAccum);
rgulR[iulRwork] = LO(dwlAccum);
dwlAccum = HI(dwlAccum) + s_ulInt32Base - 1;
}
dwlAccum += rgulR[iulRwork] - dwlMulAccum;
rgulR[iulRwork] = LO(dwlAccum);
rgulQ[iulRindex - ciulD] = QH;
// D5. Test remainder. Carry indicates result<0, therefore QH 1 too large
if (HI(dwlAccum) == 0)
{
// D6. Add back - probabilty is 2**(-31). R += D. Q[digit] -= 1
uint ulCarry;
rgulQ[iulRindex - ciulD] = QH - 1;
for (ulCarry = 0, iulDindex = 0, iulRwork = iulRindex - ciulD;
iulDindex < ciulD; iulDindex++, iulRwork++)
{
dwlAccum = rgulD[iulDindex] + (ulong)rgulR[iulRwork] + ulCarry;
ulCarry = HI(dwlAccum);
rgulR[iulRwork] = LO(dwlAccum);
}
rgulR[iulRwork] += ulCarry;
}
// D7. Loop on iulRindex
iulRindex--;
}
while (iulRindex >= ciulD);
// Normalize results
MpNormalize(rgulQ, ref ciulQ);
ciulR = ciulD;
MpNormalize(rgulR, ref ciulR);
// D8. Unnormalize: Divide D and R to get result
if (D1 > 1)
{
uint ret;
MpDiv1(rgulD, ref ciulD, D1, out ret);
MpDiv1(rgulR, ref ciulR, D1, out ret);
}
}
}