// Finished private static void MultiByteDivide(BigNumber bn1, BigNumber bn2, BigNumber outQuotient, BigNumber outRemainder) { var result = new uint[MaxLength]; var remainderLen = bn1.DataLength + 1; var remainder = new uint[remainderLen]; var mask = 0x80000000; var val = bn2.data[bn2.DataLength - 1]; var shift = 0; var resultPos = 0; while (mask != 0 && (val & mask) == 0) { ++shift; mask >>= 1; } for (var i = 0; i < bn1.DataLength; ++i) { remainder[i] = bn1.data[i]; } ShiftLeft(remainder, shift); bn2 = bn2 << shift; var j = remainderLen - bn2.DataLength; var pos = remainderLen - 1; ulong firstDivisorByte = bn2.data[bn2.DataLength - 1]; ulong secondDivisorByte = bn2.data[bn2.DataLength - 2]; var divisorLen = bn2.DataLength + 1; var dividendPart = new uint[divisorLen]; while (j > 0) { var dividend = ((ulong)remainder[pos] << 32) + remainder[pos - 1]; var qHat = dividend / firstDivisorByte; var rHat = dividend % firstDivisorByte; var done = false; while (!done) { done = true; if (qHat == 0x100000000 || qHat * secondDivisorByte > (rHat << 32) + remainder[pos - 2]) { --qHat; rHat += firstDivisorByte; if (rHat < 0x100000000) { done = false; } } } for (var h = 0; h < divisorLen; ++h) { dividendPart[h] = remainder[pos - h]; } var kk = new BigNumber(dividendPart); var ss = bn2 * (long)qHat; while (ss > kk) { --qHat; ss -= bn2; } var yy = kk - ss; for (var h = 0; h < divisorLen; ++h) { remainder[pos - h] = yy.data[bn2.DataLength - h]; } result[resultPos++] = (uint)qHat; --pos; --j; } outQuotient.DataLength = resultPos; var y = 0; for (var x = outQuotient.DataLength - 1; x >= 0; --x, ++y) { outQuotient.data[y] = result[x]; } for (; y < MaxLength; ++y) { outQuotient.data[y] = 0; } while (outQuotient.DataLength > 1 && outQuotient.data[outQuotient.DataLength - 1] == 0) { --outQuotient.DataLength; } if (outQuotient.DataLength == 0) { outQuotient.DataLength = 1; } outRemainder.DataLength = ShiftRight(remainder, shift); for (y = 0; y < outRemainder.DataLength; ++y) { outRemainder.data[y] = remainder[y]; } for (; y < MaxLength; ++y) { outRemainder.data[y] = 0; } }
// Finished private static void SingleByteDivide(BigNumber bn1, BigNumber bn2, BigNumber outQuotient, BigNumber outRemainder) { var result = new uint[MaxLength]; var resultPos = 0; for (var i = 0; i < MaxLength; ++i) { outRemainder.data[i] = bn1.data[i]; } outRemainder.DataLength = bn1.DataLength; while (outRemainder.DataLength > 1 && outRemainder.data[outRemainder.DataLength - 1] == 0) { --outRemainder.DataLength; } ulong divisor = bn2.data[0]; var pos = outRemainder.DataLength - 1; ulong dividend = outRemainder.data[pos]; if (dividend >= divisor) { var quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos] = (uint)(dividend % divisor); } --pos; while (pos >= 0) { dividend = ((ulong)outRemainder.data[pos + 1] << 32) + outRemainder.data[pos]; var quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos + 1] = 0; outRemainder.data[pos--] = (uint)(dividend % divisor); } outQuotient.DataLength = resultPos; var j = 0; for (var i = outQuotient.DataLength - 1; i >= 0; --i, ++j) { outQuotient.data[j] = result[i]; } for (; j < MaxLength; ++j) { outQuotient.data[j] = 0; } while (outQuotient.DataLength > 1 && outQuotient.data[outQuotient.DataLength - 1] == 0) { --outQuotient.DataLength; } if (outQuotient.DataLength == 0) { outQuotient.DataLength = 1; } while (outRemainder.DataLength > 1 && outRemainder.data[outRemainder.DataLength - 1] == 0) { --outRemainder.DataLength; } }
// Approved public BigNumber(string value, int radix) { var multiplier = new BigNumber(1); var result = new BigNumber(); value = value.ToUpper().Trim(); var limit = 0; if (value[0] == '-') { limit = 1; } for (var i = value.Length - 1; i >= limit; --i) { int posVal = value[i]; if (posVal >= '0' && posVal <= '9') { posVal -= '0'; } else if (posVal >= 'A' && posVal <= 'Z') { posVal = posVal - 'A' + 10; } else { posVal = 9999999; } if (posVal >= radix) { throw new ArithmeticException($"Invalid string when constructing {nameof(BigNumber)} ({value})"); } if (value[0] == '-') { posVal = -posVal; } result = result + multiplier * posVal; if (i - 1 >= limit) { multiplier = multiplier * radix; } } if (value[0] == '-') { if ((result.data[MaxLength - 1] & 0x80000000) == 0) { throw new ArithmeticException($"Negative underflow while constructing {nameof(BigNumber)}"); } } else { if ((result.data[MaxLength - 1] & 0x80000000) != 0) { throw new ArithmeticException($"Positive overflow while constructing {nameof(BigNumber)}"); } } data = new uint[MaxLength]; for (var i = 0; i < result.DataLength; ++i) { data[i] = result.data[i]; } DataLength = result.DataLength; }
// Approved public static BigNumber operator *(BigNumber bn1, BigNumber bn2) { var lastPos = MaxLength - 1; var bn1IsNegative = false; var bn2IsNegative = false; try { if ((bn1.data[lastPos] & 0x80000000) != 0) { bn1IsNegative = true; bn1 = -bn1; } if ((bn2.data[lastPos] & 0x80000000) != 0) { bn2IsNegative = true; bn2 = -bn2; } } catch { // ignored } var result = new BigNumber(); try { for (var i = 0; i < bn1.DataLength; ++i) { if (bn1.data[i] == 0) { continue; } ulong mcarry = 0; for (int j = 0, k = i; j < bn2.DataLength; ++j, ++k) { ulong bn1Data = bn1.data[i]; ulong bn2Data = bn2.data[j]; var val = bn1Data * bn2Data + result.data[k] + mcarry; result.data[k] = (uint)(val & 0xffffffff); mcarry = val >> 32; } if (mcarry != 0) { result.data[i + bn2.DataLength] = (uint)mcarry; } } } catch { throw new ArithmeticException("Multiplication overflow"); } result.DataLength = bn1.DataLength + bn2.DataLength; if (result.DataLength > MaxLength) { result.DataLength = MaxLength; } while (result.DataLength > 1 && result.data[result.DataLength - 1] == 0) { --result.DataLength; } if ((result.data[lastPos] & 0x80000000) != 0) { // ReSharper disable once InvertIf if (bn1IsNegative != bn2IsNegative && result.data[lastPos] == 0x80000000) { if (result.DataLength == 1) { return(result); } var isMaxNegative = true; for (var i = 0; i < result.DataLength - 1 && isMaxNegative; ++i) { if (result.data[i] != 0) { isMaxNegative = false; } } if (isMaxNegative) { return(result); } } throw new ArithmeticException("Multiplication overflow"); } if (bn1IsNegative != bn2IsNegative) { return(-result); } return(result); }
// Approved public BigNumber(BigNumber bn) { SetValue(bn); }
// Finished private static BigNumber BarrettReduction(BigNumber x, BigNumber n, BigNumber constant) { var k = n.DataLength; var kPlusOne = k + 1; var kMinusOne = k - 1; var q1 = new BigNumber(); for (int i = kMinusOne, j = 0; i < x.DataLength; ++i, ++j) { q1.data[j] = x.data[i]; } q1.DataLength = x.DataLength - kMinusOne; if (q1.DataLength <= 0) { q1.DataLength = 1; } var q2 = q1 * constant; var q3 = new BigNumber(); for (int i = kPlusOne, j = 0; i < q2.DataLength; ++i, ++j) { q3.data[j] = q2.data[i]; } q3.DataLength = q2.DataLength - kPlusOne; if (q3.DataLength <= 0) { q3.DataLength = 1; } //Approved to here var r1 = new BigNumber(); var lengthToCopy = x.DataLength > kPlusOne ? kPlusOne : x.DataLength; for (var i = 0; i < lengthToCopy; ++i) { r1.data[i] = x.data[i]; } r1.DataLength = lengthToCopy; var r2 = new BigNumber(); for (var i = 0; i < q3.DataLength; ++i) { if (q3.data[i] == 0) { continue; } ulong mcarry = 0; var t = i; for (var j = 0; j < n.DataLength && t < kPlusOne; ++j, ++t) { ulong q3Data = q3.data[i]; ulong nData = n.data[j]; var val = q3Data * nData + r2.data[t] + mcarry; r2.data[t] = (uint)(val & 0xffffffff); mcarry = val >> 32; } if (t < kPlusOne) { r2.data[t] = (uint)mcarry; } } r2.DataLength = kPlusOne; while (r2.DataLength > 1 && r2.data[r2.DataLength - 1] == 0) { --r2.DataLength; } r1 -= r2; if ((r1.data[MaxLength - 1] & 0x80000000) != 0) { var val = new BigNumber(); val.data[kPlusOne] = 0x1; val.DataLength = kPlusOne + 1; r1 += val; } while (r1 >= n) { r1 -= n; } return(r1); }
// Finished public BigNumber ModPow(BigNumber exp, BigNumber n) { if ((exp.data[MaxLength - 1] & 0x80000000) != 0) { throw new ArithmeticException("Positive exponents only"); } var resultNum = (BigNumber)1; BigNumber tempNum; var thisNegative = false; if ((data[MaxLength - 1] & 0x80000000) != 0) { tempNum = -this % n; thisNegative = true; } else { tempNum = this % n; } var constant = new BigNumber(); var i = n.DataLength << 1; constant.data[i] = 0x00000001; constant.DataLength = i + 1; constant = constant / n; var totalBits = exp.BitCount(); var count = 0; for (var pos = 0; pos < exp.DataLength; ++pos) { uint mask = 0x1; for (var index = 0; index < 32; ++index) { if ((exp.data[pos] & mask) != 0) { resultNum = BarrettReduction(resultNum * tempNum, n, constant); } mask <<= 1; tempNum = BarrettReduction(tempNum * tempNum, n, constant); if (tempNum.DataLength == 1 && tempNum.data[0] == 1) { if (thisNegative && (exp.data[0] & 0x1) != 0) { return(-resultNum); } return(resultNum); } ++count; if (count == totalBits) { break; } } } if (thisNegative && (exp.data[0] & 0x1) != 0) { return(-resultNum); } return(resultNum); }