Variable Bit Width Big Unsigned or Signed Integer 32,64,128,256,512,1024,204,4096 Bit. (Experimental)
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Text; [Serializable] [TypeConverter(typeof(BigIntXConverter))] [DebuggerDisplay("{DDisplay}")] public struct BigIntX : IComparable<BigIntX>, IComparable, IEquatable<BigIntX>, IConvertible, IFormattable { internal DigitsArray _digitsArray; public BigIntX(byte number) : this((ulong)number) { } public BigIntX(sbyte number) : this((long)number) { } public BigIntX(bool number) : this((ulong)(number ? 1 : 0)) { } public BigIntX(char number) : this((ulong)number) { } public BigIntX(short number) : this((long)number) { } public BigIntX(ushort number) : this((ulong)number) { } public BigIntX(int number) : this((long)number) { } public BigIntX(uint number) : this((ulong)number) { } public BigIntX(BigDecimal number) { var array = number.UnscaledValue.ToByteArray(); var length = array.Length; var offset = 0; var estSize = length / 4; var leftOver = length & 3; if (leftOver != 0) ++estSize; _digitsArray = new DigitsArray(estSize + 1, 0); for (int i = offset + length - 1, j = 0; i - offset >= 3; i -= 4, j++) { _digitsArray[j] = (uint)((array[i - 3] << 24) + (array[i - 2] << 16) + (array[i - 1] << 8) + array[i]); _digitsArray.DataUsed++; } uint accumulator = 0; for (var i = leftOver; i > 0; i--) { uint digit = array[offset + leftOver - i]; digit = digit << ((i - 1) * 8); accumulator |= digit; } _digitsArray[_digitsArray.DataUsed] = accumulator; _digitsArray.ResetDataUsed(); } public BigIntX(BigInteger number) { var array = number.ToByteArray().Invert(); var length = array.Length; var offset = 0; var estSize = length / 4; var leftOver = length & 3; if (leftOver != 0) ++estSize; _digitsArray = new DigitsArray(estSize + 1, 0); for (int i = offset + length - 1, j = 0; i - offset >= 3; i -= 4, j++) { _digitsArray[j] = (uint)((array[i - 3] << 24) + (array[i - 2] << 16) + (array[i - 1] << 8) + array[i]); _digitsArray.DataUsed++; } uint accumulator = 0; for (var i = leftOver; i > 0; i--) { uint digit = array[offset + leftOver - i]; digit = digit << ((i - 1) * 8); accumulator |= digit; } _digitsArray[_digitsArray.DataUsed] = accumulator; _digitsArray.ResetDataUsed(); } public BigIntX(decimal number) { var bits = decimal.GetBits(decimal.Truncate(number)); var length = 3; while (length > 0 && bits[length - 1] == 0) --length; var IsNegative = (bits[3] & int.MinValue) != 0; _digitsArray = new DigitsArray(3, 0); if (length == 0) { ConstructFrom(new byte[] { 0 }, 0, 1); return; } if (length == 1) { if (!IsNegative) { _digitsArray.Data[0] = (uint)bits[0]; } else { _digitsArray.Data[0] = DigitsArray.AllBits - (uint)bits[0] + 1; _digitsArray.Data[1] = DigitsArray.AllBits - (uint)bits[1]; _digitsArray.Data[2] = DigitsArray.AllBits - (uint)bits[2]; _digitsArray.Data[3] = DigitsArray.AllBits; } _digitsArray.ResetDataUsed(); return; } if (length == 2) { if (!IsNegative) { _digitsArray.Data[0] = (uint)bits[0]; _digitsArray.Data[1] = (uint)bits[1]; } else { _digitsArray.Data[0] = DigitsArray.AllBits - (uint)bits[0] + 1; _digitsArray.Data[1] = DigitsArray.AllBits - (uint)bits[1]; _digitsArray.Data[2] = DigitsArray.AllBits - (uint)bits[2]; _digitsArray.Data[3] = DigitsArray.AllBits; } _digitsArray.ResetDataUsed(); return; } if (length == 3) { if (!IsNegative) { _digitsArray.Data[0] = (uint)bits[0]; _digitsArray.Data[1] = (uint)bits[1]; _digitsArray.Data[2] = (uint)bits[2]; } else { _digitsArray.Data[0] = DigitsArray.AllBits - (uint)bits[0] + 1; _digitsArray.Data[1] = DigitsArray.AllBits - (uint)bits[1]; _digitsArray.Data[2] = DigitsArray.AllBits - (uint)bits[2]; _digitsArray.Data[3] = DigitsArray.AllBits; } _digitsArray.ResetDataUsed(); } } public BigIntX(double value) : this((decimal)value) { } public BigIntX(float value) : this((decimal)value) { } public BigIntX(Guid value) : this(value.ToByteArray()) { } public BigIntX(long number) { _digitsArray = new DigitsArray(8 / DigitsArray.DataSizeOf + 1, 0); while (number != 0 && _digitsArray.DataUsed < _digitsArray.Count) { _digitsArray[_digitsArray.DataUsed] = (uint)(number & DigitsArray.AllBits); number >>= DigitsArray.DataSizeBits; _digitsArray.DataUsed++; } _digitsArray.ResetDataUsed(); } public BigIntX(ulong number) { _digitsArray = new DigitsArray(8 / DigitsArray.DataSizeOf + 1, 0); while (number != 0 && _digitsArray.DataUsed < _digitsArray.Count) { _digitsArray[_digitsArray.DataUsed] = (uint)(number & DigitsArray.AllBits); number >>= DigitsArray.DataSizeBits; _digitsArray.DataUsed++; } _digitsArray.ResetDataUsed(); } public BigIntX(byte[] array) : this(array, 0, array.Length) { } public BigIntX(byte[] array, int length) : this(array, 0, length) { } public BigIntX(byte[] array, int offset, int length) { var estSize = length / 4; var leftOver = length & 3; if (leftOver != 0) ++estSize; _digitsArray = new DigitsArray(estSize + 1, 0); for (int i = offset + length - 1, j = 0; i - offset >= 3; i -= 4, j++) { _digitsArray[j] = (uint)((array[i - 3] << 24) + (array[i - 2] << 16) + (array[i - 1] << 8) + array[i]); _digitsArray.DataUsed++; } uint accumulator = 0; for (var i = leftOver; i > 0; i--) { uint digit = array[offset + leftOver - i]; digit = digit << ((i - 1) * 8); accumulator |= digit; } _digitsArray[_digitsArray.DataUsed] = accumulator; _digitsArray.ResetDataUsed(); } public BigIntX(string digits) : this(digits, 10) { } public BigIntX(string digits, int radix) { if (digits == null) throw new ArgumentNullException("digits"); var multiplier = new BigIntX(1); var result = new BigIntX(); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int)digits[idx]; if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else throw new ArgumentOutOfRangeException("digits"); if (d >= radix) throw new ArgumentOutOfRangeException("digits"); result += multiplier * d; multiplier *= radix; } if (digits[0] == '-') result = -result; _digitsArray = result._digitsArray; } private BigIntX(DigitsArray digits) { digits.ResetDataUsed(); _digitsArray = digits; } public BigIntX(xIntX value) : this(value.ToByteArray().Invert(), 0, value.ToByteArray().Length) { } internal BigIntX(uint[] rgu) { _digitsArray = new DigitsArray(1, 1); _digitsArray.Data = rgu; _digitsArray.ResetDataUsed(); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public bool IsNegative => _digitsArray.IsNegative; public bool IsZero => _digitsArray.IsZero; public int Sign => _digitsArray.Sign; public bool IsPowerOfTwo { get { if (Sign != 1) return false; var index = Length(_digitsArray.Data) - 1; if (((int)_digitsArray.Data[index] & ((int)_digitsArray.Data[index] - 1)) != 0) return false; while (--index >= 0) if (_digitsArray.Data[index] != 0U) return false; return true; } } public bool IsOne => this == 1; public bool IsEven => this % 2 == 0; int IComparable.CompareTo(object obj) { return Compare(this, (BigIntX)obj); } public int CompareTo(BigIntX value) { return Compare(this, value); } TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } public object ToType(Type conversionType, IFormatProvider provider) { object value; if (TryConvert(conversionType, provider, out value)) return value; throw new InvalidCastException(); } bool IConvertible.ToBoolean(IFormatProvider provider) { return bool.Parse(ToString()); } byte IConvertible.ToByte(IFormatProvider provider) { return byte.Parse(ToString()); } char IConvertible.ToChar(IFormatProvider provider) { return char.Parse(ToString()); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { return DateTime.Parse(ToString()); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return decimal.Parse(ToString()); } double IConvertible.ToDouble(IFormatProvider provider) { return double.Parse(ToString()); } short IConvertible.ToInt16(IFormatProvider provider) { return short.Parse(ToString()); } ushort IConvertible.ToUInt16(IFormatProvider provider) { return ushort.Parse(ToString()); } int IConvertible.ToInt32(IFormatProvider provider) { return int.Parse(ToString()); } uint IConvertible.ToUInt32(IFormatProvider provider) { return uint.Parse(ToString()); } long IConvertible.ToInt64(IFormatProvider provider) { return long.Parse(ToString()); } ulong IConvertible.ToUInt64(IFormatProvider provider) { return ulong.Parse(ToString()); } sbyte IConvertible.ToSByte(IFormatProvider provider) { return sbyte.Parse(ToString()); } float IConvertible.ToSingle(IFormatProvider provider) { return float.Parse(ToString()); } string IConvertible.ToString(IFormatProvider provider) { return ToString(null, provider); } public bool Equals(BigIntX obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; var c = obj; if (_digitsArray.DataUsed != c._digitsArray.DataUsed) return false; for (var idx = 0; idx < _digitsArray.DataUsed; idx++) if (_digitsArray[idx] != c._digitsArray[idx]) return false; return true; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider == null) formatProvider = CultureInfo.CurrentCulture; if (!string.IsNullOrEmpty(format)) { var ch = format[0]; if (ch == 'x' || ch == 'X') if (int.TryParse(format.Substring(1).Trim(), out var min)) return ToHexString(); if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd') throw new NotSupportedException("Not supported format: " + format); } return ToString(10); } public bool TryConvert(Type conversionType, IFormatProvider provider, out object value) { if (conversionType == typeof(bool)) { value = bool.Parse(ToString()); return true; } if (conversionType == typeof(byte)) { value = byte.Parse(ToString()); return true; } if (conversionType == typeof(char)) { value = char.Parse(ToString()); return true; } if (conversionType == typeof(decimal)) { value = decimal.Parse(ToString()); return true; } if (conversionType == typeof(double)) { value = double.Parse(ToString()); return true; } if (conversionType == typeof(short)) { value = short.Parse(ToString()); return true; } if (conversionType == typeof(int)) { value = int.Parse(ToString()); return true; } if (conversionType == typeof(long)) { value = long.Parse(ToString()); return true; } if (conversionType == typeof(sbyte)) { value = sbyte.Parse(ToString()); return true; } if (conversionType == typeof(float)) { value = float.Parse(ToString()); return true; } if (conversionType == typeof(string)) { value = ToString(null, provider); return true; } if (conversionType == typeof(ushort)) { value = ushort.Parse(ToString()); return true; } if (conversionType == typeof(uint)) { value = uint.Parse(ToString()); return true; } if (conversionType == typeof(ulong)) { value = ulong.Parse(ToString()); return true; } if (conversionType == typeof(byte[])) { value = ToByteArray(); return true; } if (conversionType == typeof(Guid)) { value = new Guid(ToByteArray()); return true; } value = null; return false; } private void ConstructFrom(byte[] array, int offset, int length) { if (array == null) throw new ArgumentNullException("Array is null"); if (offset > array.Length || length > array.Length) throw new ArgumentOutOfRangeException("Offset exceeds length"); if (length > array.Length || offset + length > array.Length) throw new ArgumentOutOfRangeException("Length exceeds array length"); var estSize = length / 4; var leftOver = length & 3; if (leftOver != 0) ++estSize; _digitsArray = new DigitsArray(estSize + 1, 0); for (int i = offset + length - 1, j = 0; i - offset >= 3; i -= 4, j++) { _digitsArray[j] = (uint)((array[i - 3] << 24) + (array[i - 2] << 16) + (array[i - 1] << 8) + array[i]); _digitsArray.DataUsed++; } uint accumulator = 0; for (var i = leftOver; i > 0; i--) { uint digit = array[offset + leftOver - i]; digit = digit << ((i - 1) * 8); accumulator |= digit; } _digitsArray[_digitsArray.DataUsed] = accumulator; _digitsArray.ResetDataUsed(); } private void Construct(string digits, int radix) { if (digits == null) throw new ArgumentNullException("digits"); var multiplier = new BigIntX(1); var result = new BigIntX(); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int)digits[idx]; if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else throw new ArgumentOutOfRangeException("digits"); if (d >= radix) throw new ArgumentOutOfRangeException("digits"); result += multiplier * d; multiplier *= radix; } if (digits[0] == '-') result = -result; _digitsArray = result._digitsArray; } public static bool TryParseNum(string digits, int radix, out BigIntX result) { result = new BigIntX(); if (digits == null) return false; var multiplier = new BigIntX(1); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int)digits[idx]; if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else return false; if (d >= radix) return false; result += multiplier * d; multiplier *= radix; } if (digits[0] == '-') result = -result; return true; } public static BigIntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static BigIntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static BigIntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static BigIntX Parse(string value, NumberStyles style, IFormatProvider provider) { if (!TryParse(value, style, provider, out var result)) throw new Exception($"TryParse value {value} failure."); return result; } public static bool TryParse(string value, out BigIntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out BigIntX result) { result = new BigIntX(); if (string.IsNullOrEmpty(value)) return false; if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(1); } else { if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(2); } } if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier) return TryParseNum(value, 16, out result); return TryParseNum(value, 10, out result); } public static implicit operator BigIntX(BigDecimal value) { return new BigIntX(value); } public static implicit operator BigIntX(BigInteger value) { return new BigIntX(value); } public static implicit operator BigIntX(xIntX value) { return new BigIntX(value); } public static implicit operator BigIntX(bool value) { return new BigIntX(value); } public static implicit operator BigIntX(byte value) { return new BigIntX(value); } public static implicit operator BigIntX(char value) { return new BigIntX(value); } public static explicit operator BigIntX(decimal value) { return new BigIntX(value); } public static explicit operator BigIntX(double value) { return new BigIntX(value); } public static explicit operator BigIntX(float value) { return new BigIntX(value); } public static implicit operator BigIntX(short value) { return new BigIntX(value); } public static implicit operator BigIntX(ushort value) { return new BigIntX(value); } public static implicit operator BigIntX(sbyte value) { return new BigIntX(value); } public static implicit operator BigIntX(long value) { return new BigIntX(value); } public static implicit operator BigIntX(ulong value) { return new BigIntX(value); } public static implicit operator BigIntX(int value) { return new BigIntX(value); } public static explicit operator byte(BigIntX value) { return checked((byte)(int)value); } public static explicit operator sbyte(BigIntX value) { return checked((sbyte)(int)value); } public static implicit operator BigIntX(uint value) { return new BigIntX((ulong)value); } public static explicit operator short(BigIntX value) { return checked((short)(int)value); } public static explicit operator ushort(BigIntX value) { return checked((ushort)(int)value); } public static explicit operator BigInteger(BigIntX value) { return new BigInteger(value.ToByteArray()); } public static explicit operator xIntX(BigIntX value) { return new xIntX(value.ToByteArray()); } public static explicit operator int(BigIntX value) { if (value.IsZero) return 0; if (value.Sign > 0) return checked((int)value._digitsArray.Data[0]); if (value > int.MaxValue) throw new OverflowException("int overflow."); return -(int)value._digitsArray.Data[0]; } public static explicit operator uint(BigIntX value) { return value._digitsArray.Data[0]; } public static explicit operator long(BigIntX value) { if (value > long.MaxValue) throw new OverflowException("long overflow."); var uu = value._digitsArray.Data.Length - 1 <= 1 ? value._digitsArray.Data[0] : ((ulong)value._digitsArray.Data[1] << 32) | value._digitsArray.Data[0]; var ll = value.Sign > 0 ? (long)uu : -(long)uu; if (ll > 0L && value.Sign > 0 || ll < 0L && value.Sign < 0) return ll; throw new OverflowException("long overflow."); } public static explicit operator ulong(BigIntX value) { if (value > ulong.MaxValue) throw new OverflowException("ulong overflow."); if (value._digitsArray.Data.Length - 1 > 1) return ((ulong)value._digitsArray.Data[1] << 32) | value._digitsArray.Data[0]; return value._digitsArray.Data[0]; } public static decimal ToDecimal(BigIntX value) { var len = value._digitsArray.Data.Length - 1; if (len > 3) throw new OverflowException("Decimal overflow."); ; var lo = 0; var mid = 0; var hi = 0; if (len > 2) hi = (int)value._digitsArray.Data[2]; if (len > 1) mid = (int)value._digitsArray.Data[1]; if (len > 0) lo = (int)value._digitsArray.Data[0]; return new decimal(lo, mid, hi, value.Sign < 0, 0); } public static explicit operator decimal(BigIntX value) { var len = value._digitsArray.Data.Length - 1; if (len > 3) throw new OverflowException("Decimal overflow."); ; var lo = 0; var mid = 0; var hi = 0; if (len > 2) hi = (int)value._digitsArray.Data[2]; if (len > 1) mid = (int)value._digitsArray.Data[1]; if (len > 0) lo = (int)value._digitsArray.Data[0]; return new decimal(lo, mid, hi, value.Sign < 0, 0); } public static BigIntX operator +(BigIntX left, BigIntX right) { var size = Math.Max(left._digitsArray.DataUsed, right._digitsArray.DataUsed); var da = new DigitsArray(size + 1); long carry = 0; for (var i = 0; i < da.Count; i++) { var sum = left._digitsArray[i] + (long)right._digitsArray[i] + carry; carry = sum >> DigitsArray.DataSizeBits; da[i] = (uint)(sum & DigitsArray.AllBits); } return new BigIntX(da); } public static BigIntX Add(BigIntX left, BigIntX right) { return left + right; } public static BigIntX operator ++(BigIntX left) { return left + 1; } public static BigIntX Increment(BigIntX left) { return left + 1; } public static BigIntX operator -(BigIntX left, BigIntX right) { var size = Math.Max(left._digitsArray.DataUsed, right._digitsArray.DataUsed) + 1; var da = new DigitsArray(size); long carry = 0; for (var i = 0; i < da.Count; i++) { var diff = left._digitsArray[i] - (long)right._digitsArray[i] - carry; da[i] = (uint)(diff & DigitsArray.AllBits); da.DataUsed++; carry = diff < 0 ? 1 : 0; } return new BigIntX(da); } public static BigIntX Subtract(BigIntX left, BigIntX right) { return left - right; } public static BigIntX operator --(BigIntX left) { return left - 1; } public static BigIntX Decrement(BigIntX left) { return left - 1; } public static BigIntX operator -(BigIntX left) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (left.IsZero) return new BigIntX(0); var da = new DigitsArray(left._digitsArray.DataUsed + 1, left._digitsArray.DataUsed + 1); for (var i = 0; i < da.Count; i++) da[i] = ~left._digitsArray[i]; var carry = true; var index = 0; while (carry && index < da.Count) { var val = (long)da[index] + 1; da[index] = (uint)(val & DigitsArray.AllBits); carry = val >> DigitsArray.DataSizeBits > 0; index++; } return new BigIntX(da); } public BigIntX Negate() { return -this; } public static BigIntX Abs(BigIntX left) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (left.IsNegative) return -left; return left; } public static BigIntX operator *(BigIntX left, BigIntX right) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); var leftSideNeg = left.IsNegative; var rightSideNeg = right.IsNegative; left = Abs(left); right = Abs(right); var da = new DigitsArray(left._digitsArray.DataUsed + right._digitsArray.DataUsed); da.DataUsed = da.Count; for (var i = 0; i < left._digitsArray.DataUsed; i++) { ulong carry = 0; for (int j = 0, k = i; j < right._digitsArray.DataUsed; j++, k++) { var val = left._digitsArray[i] * (ulong)right._digitsArray[j] + da[k] + carry; da[k] = (uint)(val & DigitsArray.AllBits); carry = val >> DigitsArray.DataSizeBits; } if (carry != 0) da[i + right._digitsArray.DataUsed] = (uint)carry; } var result = new BigIntX(da); return leftSideNeg != rightSideNeg ? -result : result; } public static BigIntX Multiply(BigIntX left, BigIntX right) { return left * right; } public static BigIntX operator /(BigIntX left, BigIntX right) { if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); if (right.IsZero) throw new DivideByZeroException(); var divisorNeg = right.IsNegative; var dividendNeg = left.IsNegative; left = Abs(left); right = Abs(right); if (left < right) return new BigIntX(0); Divide(left, right, out var quotient, out var remainder); return dividendNeg != divisorNeg ? -quotient : quotient; } public static BigIntX Divide(BigIntX left, BigIntX right) { return left / right; } private static void Divide(BigIntX left, BigIntX right, out BigIntX quotient, out BigIntX remainder) { if (left.IsZero) { quotient = new BigIntX(); remainder = new BigIntX(); return; } if (right._digitsArray.DataUsed == 1) SingleDivide(left, right, out quotient, out remainder); else MultiDivide(left, right, out quotient, out remainder); } private static void MultiDivide(BigIntX left, BigIntX right, out BigIntX quotient, out BigIntX remainder) { if (right.IsZero) throw new DivideByZeroException(); var val = right._digitsArray[right._digitsArray.DataUsed - 1]; var d = 0; for (var mask = DigitsArray.HiBitSet; mask != 0 && (val & mask) == 0; mask >>= 1) d++; var remainderLen = left._digitsArray.DataUsed + 1; var remainderDat = new uint[remainderLen]; left._digitsArray.CopyTo(remainderDat, 0, left._digitsArray.DataUsed); DigitsArray.ShiftLeft(remainderDat, d); right = right << d; ulong firstDivisor = right._digitsArray[right._digitsArray.DataUsed - 1]; ulong secondDivisor = right._digitsArray.DataUsed < 2 ? 0 : right._digitsArray[right._digitsArray.DataUsed - 2]; var divisorLen = right._digitsArray.DataUsed + 1; var dividendPart = new DigitsArray(divisorLen, divisorLen); var result = new uint[left._digitsArray.Count + 1]; var resultPos = 0; var carryBit = (ulong)0x1 << DigitsArray.DataSizeBits; for (int j = remainderLen - right._digitsArray.DataUsed, pos = remainderLen - 1; j > 0; j--, pos--) { var dividend = ((ulong)remainderDat[pos] << DigitsArray.DataSizeBits) + remainderDat[pos - 1]; var qHat = dividend / firstDivisor; var rHat = dividend % firstDivisor; while (pos >= 2) { if (qHat == carryBit || qHat * secondDivisor > (rHat << DigitsArray.DataSizeBits) + remainderDat[pos - 2]) { qHat--; rHat += firstDivisor; if (rHat < carryBit) continue; } break; } for (var h = 0; h < divisorLen; h++) dividendPart[divisorLen - h - 1] = remainderDat[pos - h]; var dTemp = new BigIntX(dividendPart); var rTemp = right * (long)qHat; while (rTemp > dTemp) { qHat--; rTemp -= right; } rTemp = dTemp - rTemp; for (var h = 0; h < divisorLen; h++) remainderDat[pos - h] = rTemp._digitsArray[right._digitsArray.DataUsed - h]; result[resultPos++] = (uint)qHat; } Array.Reverse(result, 0, resultPos); quotient = new BigIntX(new DigitsArray(result)); var n = DigitsArray.ShiftRight(remainderDat, d); var rDA = new DigitsArray(n, n); rDA.CopyFrom(remainderDat, 0, 0, rDA.DataUsed); remainder = new BigIntX(rDA); } private static void SingleDivide(BigIntX left, BigIntX right, out BigIntX quotient, out BigIntX remainder) { if (right.IsZero) throw new DivideByZeroException(); var remainderDigits = new DigitsArray(left._digitsArray); remainderDigits.ResetDataUsed(); var pos = remainderDigits.DataUsed - 1; var divisor = (ulong)right._digitsArray[0]; var dividend = (ulong)remainderDigits[pos]; var result = new uint[left._digitsArray.Count]; left._digitsArray.CopyTo(result, 0, result.Length); var resultPos = 0; if (dividend >= divisor) { result[resultPos++] = (uint)(dividend / divisor); remainderDigits[pos] = (uint)(dividend % divisor); } pos--; while (pos >= 0) { dividend = ((ulong)remainderDigits[pos + 1] << DigitsArray.DataSizeBits) + remainderDigits[pos]; result[resultPos++] = (uint)(dividend / divisor); remainderDigits[pos + 1] = 0; remainderDigits[pos--] = (uint)(dividend % divisor); } remainder = new BigIntX(remainderDigits); var quotientDigits = new DigitsArray(resultPos + 1, resultPos); var j = 0; for (var i = quotientDigits.DataUsed - 1; i >= 0; i--, j++) quotientDigits[j] = result[i]; quotient = new BigIntX(quotientDigits); } public static BigIntX operator %(BigIntX left, BigIntX right) { if (left == null) throw new ArgumentNullException("left"); if (right == null) throw new ArgumentNullException("right"); if (right.IsZero) throw new DivideByZeroException(); BigIntX quotient; BigIntX remainder; var dividendNeg = left.IsNegative; left = Abs(left); right = Abs(right); if (left < right) return left; Divide(left, right, out quotient, out remainder); return dividendNeg ? -remainder : remainder; } public static BigIntX Modulus(BigIntX left, BigIntX right) { return left % right; } public BigIntX Pow(BigIntX power) { return Pow(this, power); } public static BigIntX operator &(BigIntX left, BigIntX right) { var len = Math.Max(left._digitsArray.DataUsed, right._digitsArray.DataUsed); var da = new DigitsArray(len, len); for (var idx = 0; idx < len; idx++) da[idx] = left._digitsArray[idx] & right._digitsArray[idx]; return new BigIntX(da); } public static BigIntX BitwiseAnd(BigIntX left, BigIntX right) { return left & right; } public static BigIntX operator |(BigIntX left, BigIntX right) { var len = Math.Max(left._digitsArray.DataUsed, right._digitsArray.DataUsed); var da = new DigitsArray(len, len); for (var idx = 0; idx < len; idx++) da[idx] = left._digitsArray[idx] | right._digitsArray[idx]; return new BigIntX(da); } public static BigIntX BitwiseOr(BigIntX left, BigIntX right) { return left | right; } public static BigIntX operator ^(BigIntX left, BigIntX right) { var len = Math.Max(left._digitsArray.DataUsed, right._digitsArray.DataUsed); var da = new DigitsArray(len, len); for (var idx = 0; idx < len; idx++) da[idx] = left._digitsArray[idx] ^ right._digitsArray[idx]; return new BigIntX(da); } public static BigIntX Xor(BigIntX left, BigIntX right) { return left ^ right; } public static BigIntX operator ~(BigIntX left) { var da = new DigitsArray(left._digitsArray.Count); for (var idx = 0; idx < da.Count; idx++) da[idx] = ~left._digitsArray[idx]; return new BigIntX(da); } public static BigIntX OnesComplement(BigIntX left) { return ~left; } public static BigIntX operator <<(BigIntX left, int shiftCount) { if (left == null) throw new ArgumentNullException("left"); var da = new DigitsArray(left._digitsArray); da.DataUsed = da.ShiftLeftWithoutOverflow(shiftCount); return new BigIntX(da); } public static BigIntX LeftShift(BigIntX left, int shiftCount) { return left << shiftCount; } public static BigIntX operator >> (BigIntX left, int shiftCount) { if (left == null) throw new ArgumentNullException("left"); var da = new DigitsArray(left._digitsArray); da.DataUsed = da.ShiftRight(shiftCount); if (left.IsNegative) { for (var i = da.Count - 1; i >= da.DataUsed; i--) da[i] = DigitsArray.AllBits; var mask = DigitsArray.HiBitSet; for (var i = 0; i < DigitsArray.DataSizeBits; i++) { if ((da[da.DataUsed - 1] & mask) == DigitsArray.HiBitSet) break; da[da.DataUsed - 1] |= mask; mask >>= 1; } da.DataUsed = da.Count; } return new BigIntX(da); } public static BigIntX RightShift(BigIntX left, int shiftCount) { if (left == null) throw new ArgumentNullException("left"); return left >> shiftCount; } public static int Compare(BigIntX left, BigIntX right) { if (ReferenceEquals(left, right)) return 0; if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); if (left > right) return 1; if (left == right) return 0; return -1; } public static bool operator ==(BigIntX left, BigIntX right) { if (ReferenceEquals(left, right)) return true; if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) return false; if (left.IsNegative != right.IsNegative) return false; return left.Equals(right); } public static bool operator !=(BigIntX left, BigIntX right) { return !(left == right); } public static bool operator >(BigIntX left, BigIntX right) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); if (left.IsNegative != right.IsNegative) return right.IsNegative; if (left._digitsArray.DataUsed != right._digitsArray.DataUsed) return left._digitsArray.DataUsed > right._digitsArray.DataUsed; for (var idx = left._digitsArray.DataUsed - 1; idx >= 0; idx--) if (left._digitsArray[idx] != right._digitsArray[idx]) return left._digitsArray[idx] > right._digitsArray[idx]; return false; } public static bool operator <(BigIntX left, BigIntX right) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); if (left.IsNegative != right.IsNegative) return left.IsNegative; if (left._digitsArray.DataUsed != right._digitsArray.DataUsed) return left._digitsArray.DataUsed < right._digitsArray.DataUsed; for (var idx = left._digitsArray.DataUsed - 1; idx >= 0; idx--) if (left._digitsArray[idx] != right._digitsArray[idx]) return left._digitsArray[idx] < right._digitsArray[idx]; return false; } public static bool operator >=(BigIntX left, BigIntX right) { return Compare(left, right) >= 0; } public static bool operator <=(BigIntX left, BigIntX right) { return Compare(left, right) <= 0; } public override bool Equals(object obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; var c = (BigIntX)obj; if (_digitsArray.DataUsed != c._digitsArray.DataUsed) return false; for (var idx = 0; idx < _digitsArray.DataUsed; idx++) if (_digitsArray[idx] != c._digitsArray[idx]) return false; return true; } public override int GetHashCode() { var hash = 0x811c9dc5; for (var i = 0; i < _digitsArray.Data.Length; i++) { hash = ((hash << 13) | (hash >> 19)) ^ _digitsArray.Data[i]; hash *= 0x1000193; } return (int)hash; } public string GetDataAsString() { return _digitsArray.GetDataAsString(); } public override string ToString() { return ToString(10); } public byte[] ToByteArray() { return _digitsArray.ToByteArray(); } public uint[] ToUIn32Array() { return _digitsArray.ToUIn32Array(); } public ulong[] ToUIn64Array() { return _digitsArray.ToUIn64Array(); } public string ToString(int radix) { if (radix < 2 || radix > 36) throw new ArgumentOutOfRangeException("radix"); if (IsZero) return "0"; var a = this; var negative = a.IsNegative; a = Abs(this); BigIntX quotient; BigIntX remainder; var biRadix = new BigIntX(radix); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; var al = new ArrayList(); while (a._digitsArray.DataUsed > 1 || a._digitsArray.DataUsed == 1 && a._digitsArray[0] != 0) { Divide(a, biRadix, out quotient, out remainder); al.Insert(0, charSet[(int)remainder._digitsArray[0]]); a = quotient; } var result = new string((char[])al.ToArray(typeof(char))); if (radix == 10 && negative) return "-" + result; return result; } public string ToHexString() { var sb = new StringBuilder(); sb.AppendFormat("{0:X}", _digitsArray[_digitsArray.DataUsed - 1]); var f = "{0:X" + 2 * DigitsArray.DataSizeOf + "}"; for (var i = _digitsArray.DataUsed - 2; i >= 0; i--) sb.AppendFormat(f, _digitsArray[i]); return sb.ToString(); } public string ToBinaryString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base2 = new StringBuilder(bytes.Length * 8); var binary = Convert.ToString(bytes[index], 2); if (binary[0] != '0' && Sign == 1) base2.Append('0'); base2.Append(binary); for (index--; index >= 0; index--) base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0')); return base2.ToString(); } public string ToOctalString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8); var rem = bytes.Length % 3; if (rem == 0) rem = 3; var base0 = 0; while (rem != 0) { base0 <<= 8; base0 += bytes[index--]; rem--; } var octal = Convert.ToString(base0, 8); if (octal[0] != '0' && Sign == 1) base8.Append('0'); base8.Append(octal); while (index >= 0) { base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2]; base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0')); index -= 3; } return base8.ToString(); } public static int ToInt16(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return short.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static uint ToUInt16(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return ushort.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static int ToInt32(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return int.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static uint ToUInt32(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return uint.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static long ToInt64(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return long.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static ulong ToUInt64(BigIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); return ulong.Parse(value.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture); } public static BigIntX Pow(BigIntX value, BigIntX exponent) { if (value == null) throw new ArgumentNullException("Value cannot be null"); if (exponent == null) throw new ArgumentNullException("Exponent cannot be null"); if (exponent < 0) throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative"); BigIntX result = 1; while (exponent != 0) { if ((exponent & 1) != 0) result *= value; exponent >>= 1; value *= value; } return result; } public static BigIntX ModPow(BigIntX n, BigIntX e, BigInteger m) { var n1 = n; var e1 = e; var c = 0; if (e1 == 0) return 1; if (e1 == 1) return n1 % m; if (e1 == 2) return n1 * n1 % m; BigIntX r; n1 %= m; r = 1; if ((e1 & 1) == 1) r = n1; while (e1 > 1) { c++; e1 >>= 1; n1 = n1 * n1 % m; if ((e1 & 1) == 1) r = r * n1 % m; } return r; } public static BigIntX Sqrt(BigIntX n) { var q = (BigIntX)1 << ((int)Log(n, 2) >> 1); var m = (BigIntX)0; while (Abs(q - m) >= 1) { m = q; q = (m + n / m) >> 1; } return q; } public static double Log(BigIntX value, double baseValue) { Array.Resize(ref value._digitsArray.Data, value._digitsArray.DataUsed); var c = 0.0; var d = 0.5; var dataLength = Length(value._digitsArray.Data); var topBits = BitLengthOfUInt(value._digitsArray.Data[dataLength - 1]); var bitLength = (dataLength - 1) * 32 + topBits; var bit = (uint)(1 << (topBits - 1)); for (var index = dataLength - 1; index >= 0; --index) { for (; bit != 0U; bit >>= 1) { if (((int)value._digitsArray.Data[index] & (int)bit) != 0) c += d; d *= 0.5; } bit = 2147483648U; } return (Math.Log(c) + 0.693147180559945 * bitLength) / Math.Log(baseValue); } private static int BitLengthOfUInt(uint x) { var numBits = 0; while (x > 0) { x >>= 1; numBits++; } return numBits; } private static int Length(uint[] rgu) { var length = rgu.Length; return rgu[length - 1] != 0U ? length : length - 1; } public static List<BigIntX> GetFactors(BigIntX n) { var Factors = new List<BigIntX>(); var s = Sqrt(n); var a = (BigIntX)3; while (a < s) { if (n % a == 0) { Factors.Add(a); if (a * a != n) Factors.Add(n / a); } a += 2; } return Factors; } public static BigIntX Gcd(BigIntX a, BigIntX b) { while (b > 0) { var r = a % b; a = b; b = r; } return a; } public static BigIntX Lcm(BigIntX a, BigIntX b) { return a * b / a.Gcd(b); } public static int GetBitWidth(BigIntX value) { var bw = 1; var v = value; while ((v >>= 1) > 0) bw++; if (bw < 8) bw = 8; return bw; } public static BigIntX GetMaxValueBitWidth(int bitLength) { return ((BigIntX)1 << bitLength) - 1; } public static BigIntX GetMaxValue(BigIntX value) { var bitLength = GetBitWidth(value); return ((BigIntX)1 << bitLength) - 1; } public static BigIntX Min(BigIntX left, BigIntX right) { if (left.CompareTo(right) <= 0) return left; return right; } public static BigIntX Max(BigIntX left, BigIntX right) { if (left.CompareTo(right) < 0) return right; return left; } public static double Log10(BigIntX value) { return Log(value, 10.0); } public static double LogN(BigIntX value) { return Log(value, 2.0); } private class BigIntXConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value != null) if (TryParse($"{value}", out var i)) return i; return new BigIntX(); } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { return destinationType == typeof(string) || base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { return destinationType == typeof(string) ? $"{value}" : base.ConvertTo(context, culture, value, destinationType); } } }