public static IntegerNumber Inverse(IntegerNumber number, out IntegerNumber remainder) { int n = number.Digits; if (n <= IntegerDigitsBreakPoint) { ulong[] a = new ulong[(n + 1) * 2]; a[n * 2] = 1UL; IntegerNumber b = number.ExtendTo(n + 1); ulong[] q = new ulong[(n + 1) * 2]; AsmX64Operations.GetDivMod(a, b.bits, n + 1, q, true); remainder = new IntegerNumber(a, false); shrink(remainder); IntegerNumber result = new IntegerNumber(q, false); shrink(result); return(result); } bool isNegative = false; if (number.IsNegative) { number = -number; isNegative = true; n = number.Digits; } //Newton iteration: x <- x + x * (1 - d * x^2) int m = (n + 5) >> 1; IntegerNumber dx, dhi = number.GetDigits(n - m, m, false); IntegerNumber x = Inverse(dhi, out dx); //IntegerNumber test = x * d + dx; //shrink(ref test); //if (test.Digits != d.Digits * 2 + 1 || test.bits[d.Digits * 2] != 1UL || Enumerable.Range(0, d.Digits * 2).Any(idx => test.bits[idx] != 0UL)) //{ // System.Diagnostics.Debugger.Break(); //} const int CorrectionDigits = 4; IntegerNumber dlo = number.GetDigits(0, n - m, true); IntegerNumber dp = (dx << ((n - m) * 64)) - dlo * x; IntegerNumber delta = dp >> ((m - CorrectionDigits) * 64); //keep one additional correction digit. shrink(delta); delta *= x; delta >>= (m + CorrectionDigits) * 64; shrink(delta); x <<= (n - m) * 64; x += delta; //FACT: number * x == (IntegerNumber.One << (n * 128)) - ((dp << ((n - m) * 64)) - delta * number); //but : remainder = number * x; //then: Array.Resize(ref remainder.bits, n * 2); //then: shrink(ref remainder); //then: remainder = -remainder; remainder = ((dp - delta * dhi) << ((n - m) * 64)) - delta * dlo; shrink(remainder); int count = 0; while (remainder.IsNegative) { remainder += number; x -= One; count++; if (count >= 2) { System.Diagnostics.Debugger.Break(); } } while (remainder >= number) { remainder -= number; x += One; count++; if (count >= 2) { System.Diagnostics.Debugger.Break(); } } if (isNegative) { x = -x; remainder = -remainder; } return(x); }
public static IntegerNumber DivRem(IntegerNumber dividend, IntegerNumber divisor, out IntegerNumber remainder) { shrink(dividend); shrink(divisor); int dividendDigits = dividend.Digits; int divisorDigits = divisor.Digits; IntegerNumber quotient; int n = Math.Max(divisorDigits, (dividendDigits + 1) >> 1); if (divisorDigits <= IntegerDigitsBreakPoint) { dividend = dividend.ExtendTo(n * 2); divisor = divisor.ExtendTo(n); quotient = new IntegerNumber(new ulong[n * 2], false); remainder = new IntegerNumber(dividend.bits, true); AsmX64Operations.GetDivMod(remainder.bits, divisor.bits, n, quotient.bits, true); shrink(quotient); shrink(remainder); return(quotient); } bool sign = false; if (dividend.IsNegative != divisor.IsNegative) { dividend = IntegerNumber.Abs(dividend); divisor = IntegerNumber.Abs(divisor); sign = true; } else if (dividend.IsNegative && divisor.IsNegative) { dividend = -dividend; divisor = -divisor; } int delta = n - divisorDigits; IntegerNumber x = (divisor << (delta * 128)).Inverse(); quotient = dividend * x >> (n * 128); shrink(quotient); remainder = dividend - quotient * divisor; shrink(remainder); int count = 0; while (remainder.IsNegative) { remainder += divisor; quotient -= One; count++; if (count >= 2) { System.Diagnostics.Debugger.Break(); } } while (remainder >= divisor) { remainder -= divisor; quotient += One; count++; if (count >= 2) { System.Diagnostics.Debugger.Break(); } } if (sign) { quotient = -quotient; remainder = -remainder; } return(quotient); }