Int256 Bit Class
Jun-11,2021: Obsolete Use xIntX Instead.
using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(Int256Converter))] [DebuggerDisplay("{DDisplay}")] public struct Int256 : IComparable<Int256>, IComparable, IEquatable<Int256>, IConvertible, IFormattable { public ulong Bytes24To32; public ulong Bytes16To24; public ulong Bytes8To16; public ulong Bytes0To8; private const ulong HiNeg = 0x8000000000000000; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public static Int256 Zero = new Int256(0); public static Int256 Ten = new Int256(10); public static Int256 One = new Int256(1); public static Int256 MaxValue = GetMaxValue(); public static Int256 MinValue = GetMinValue(); private static Int256 GetMaxValue() { return new Int256(long.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue); } private static Int256 GetMinValue() { return -GetMaxValue(); } public Int256(string value) { TryParse(value, out var result); Bytes24To32 = result.Bytes24To32; Bytes16To24 = result.Bytes16To24; Bytes8To16 = result.Bytes8To16; Bytes0To8 = result.Bytes0To8; } public Int256(byte value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = value; } public Int256(bool value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = (ulong) (value ? 1 : 0); } public Int256(char value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = value; } public Int256(decimal value) { if (value < 0) { var n = -new Int256(-value); Bytes24To32 = n.Bytes24To32; Bytes16To24 = n.Bytes16To24; Bytes8To16 = n.Bytes8To16; Bytes0To8 = n.Bytes0To8; return; } var bits = decimal.GetBits(value); Bytes24To32 = 0; Bytes16To24 = (uint) bits[2]; Bytes8To16 = (uint) bits[1]; Bytes0To8 = (uint) bits[0]; if (value < 0) { Bytes24To32 = ~Bytes24To32; Bytes16To24 = ~Bytes16To24; Bytes8To16 = ~Bytes8To16; Bytes0To8 = ~Bytes0To8; } } public Int256(double value) : this((decimal) value) { } public Int256(float value) : this((decimal) value) { } public Int256(short value) { if (value < 0) { var n = -new Int256(-(value + 1)) - 1; Bytes24To32 = n.Bytes24To32; Bytes16To24 = n.Bytes16To24; Bytes8To16 = n.Bytes8To16; Bytes0To8 = n.Bytes0To8; return; } Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = (ulong) value; } public Int256(int value) { if (value < 0) { var n = -new Int256(-(value + 1)) - 1; Bytes24To32 = n.Bytes24To32; Bytes16To24 = n.Bytes16To24; Bytes8To16 = n.Bytes8To16; Bytes0To8 = n.Bytes0To8; return; } Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = (ulong) value; } public Int256(long value) { if (value < 0) { var n = -new Int256(-(value + 1)) - 1; Bytes24To32 = n.Bytes24To32; Bytes16To24 = n.Bytes16To24; Bytes8To16 = n.Bytes8To16; Bytes0To8 = n.Bytes0To8; return; } Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = (ulong) value; } public Int256(sbyte value) { if (value < 0) { var n = -new Int256(-(value + 1)) - 1; Bytes24To32 = n.Bytes24To32; Bytes16To24 = n.Bytes16To24; Bytes8To16 = n.Bytes8To16; Bytes0To8 = n.Bytes0To8; return; } Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = (ulong) value; } public Int256(ushort value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = value; } public Int256(uint value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = value; } public Int256(ulong value) { Bytes24To32 = 0; Bytes16To24 = 0; Bytes8To16 = 0; Bytes0To8 = value; } public Int256(Guid value) : this(value.ToByteArray()) { } public Int256(byte[] value) { if (value == null) throw new Exception("Value cannot be null."); if (value.Length != 32) throw new Exception("Values length must be 32 bytes."); Bytes24To32 = BitConverter.ToUInt64(value, 24); Bytes16To24 = BitConverter.ToUInt64(value, 16); Bytes8To16 = BitConverter.ToUInt64(value, 8); Bytes0To8 = BitConverter.ToUInt64(value, 0); } public Int256(ulong msbh, ulong msbl, ulong lsbh, ulong lsbl) { Bytes24To32 = msbh; Bytes16To24 = msbl; Bytes8To16 = lsbh; Bytes0To8 = lsbl; } public Int256(int sign, uint[] array) { if (array == null) throw new Exception("Array cannot be null."); var msbh = new byte[8]; var msbl = new byte[8]; var lsbh = new byte[8]; var lsbl = new byte[8]; if (array.Length > 0) { Array.Copy(BitConverter.GetBytes(array[0]), 0, lsbl, 0, 4); if (array.Length > 1) { Array.Copy(BitConverter.GetBytes(array[1]), 0, lsbl, 4, 4); if (array.Length > 2) { Array.Copy(BitConverter.GetBytes(array[2]), 0, lsbh, 0, 4); if (array.Length > 3) { Array.Copy(BitConverter.GetBytes(array[3]), 0, lsbh, 4, 4); if (array.Length > 4) { Array.Copy(BitConverter.GetBytes(array[4]), 0, msbl, 0, 4); if (array.Length > 5) { Array.Copy(BitConverter.GetBytes(array[5]), 0, msbl, 4, 4); if (array.Length > 6) { Array.Copy(BitConverter.GetBytes(array[6]), 0, msbh, 0, 4); if (array.Length > 7) Array.Copy(BitConverter.GetBytes(array[7]), 0, msbh, 4, 4); } } } } } } } Bytes24To32 = BitConverter.ToUInt64(msbh, 0); Bytes16To24 = BitConverter.ToUInt64(msbl, 0); Bytes8To16 = BitConverter.ToUInt64(lsbh, 0); Bytes0To8 = BitConverter.ToUInt64(lsbl, 0); if (sign < 0) Bytes24To32 |= HiNeg; else Bytes24To32 &= ~HiNeg; } public ulong MSBH => Bytes24To32; public ulong MSBL => Bytes16To24; public ulong LSBH => Bytes8To16; public ulong LSBL => Bytes0To8; public int BitWidth { get { Int256 bitWidth = 1; var v = this; while ((v >>= 1) > 0) bitWidth++; if (bitWidth < 8) bitWidth = 8; while (bitWidth % 8 != 0) bitWidth++; return (int) bitWidth; } } public int Sign { get { if (Bytes24To32 == 0 && Bytes16To24 == 0 && Bytes8To16 == 0 && Bytes0To8 == 0) return 0; return (Bytes24To32 & HiNeg) == 0 ? 1 : -1; } } public override int GetHashCode() { return MSBH.GetHashCode() ^ MSBL.GetHashCode() ^ LSBH.GetHashCode() ^ LSBL.GetHashCode(); } public override bool Equals(object obj) { return base.Equals(obj); } public bool Equals(Int256 obj) { return Bytes24To32 == obj.Bytes24To32 && Bytes16To24 == obj.Bytes16To24 && Bytes8To16 == obj.Bytes8To16 && Bytes0To8 == obj.Bytes0To8; } public override string ToString() { return ToString(null, null); } public string ToString(string format) { return ToString(format, null); } 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') { int.TryParse(format.Substring(1).Trim(), out var min); return ToHexString(ch == 'X', min); } if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd') throw new NotSupportedException("Not supported format: " + format); } return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo))); } private string ToHexString(bool caps, int min) { var bytes = ToByteArray().Invert(); var sb = new StringBuilder(); var x = caps ? "X" : "x"; foreach (var b in bytes) { var hex = b.ToString($"{x}2"); sb.Append(hex); } return sb.ToString(); } private string ToString(NumberFormatInfo info) { if (Sign == 0) return "0"; var sb = new StringBuilder(); var current = this; current.Bytes24To32 &= ~HiNeg; while (true) { current = DivRem(current, Ten, out var r); if (r.Bytes0To8 > 0 || current.Sign != 0 || sb.Length == 0) sb.Insert(0, (char) ('0' + r.Bytes0To8)); if (current.Sign == 0) break; } var s = sb.ToString(); if (Sign < 0 && s != "0") return info.NegativeSign + s; return s; } TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } bool IConvertible.ToBoolean(IFormatProvider provider) { return (bool) this; } byte IConvertible.ToByte(IFormatProvider provider) { return (byte) this; } char IConvertible.ToChar(IFormatProvider provider) { return (char) this; } DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return (decimal) this; } double IConvertible.ToDouble(IFormatProvider provider) { return (double) this; } short IConvertible.ToInt16(IFormatProvider provider) { return (short) this; } int IConvertible.ToInt32(IFormatProvider provider) { return (int) this; } long IConvertible.ToInt64(IFormatProvider provider) { return (int) this; } sbyte IConvertible.ToSByte(IFormatProvider provider) { return (sbyte) this; } float IConvertible.ToSingle(IFormatProvider provider) { return (float) this; } string IConvertible.ToString(IFormatProvider provider) { return ToString(null, provider); } public bool TryConvert(Type conversionType, IFormatProvider provider, out object value) { if (conversionType == typeof(bool)) { value = (bool) this; return true; } if (conversionType == typeof(byte)) { value = (byte) this; return true; } if (conversionType == typeof(char)) { value = (char) this; return true; } if (conversionType == typeof(decimal)) { value = (decimal) this; return true; } if (conversionType == typeof(double)) { value = (double) this; return true; } if (conversionType == typeof(short)) { value = (short) this; return true; } if (conversionType == typeof(int)) { value = (int) this; return true; } if (conversionType == typeof(long)) { value = (long) this; return true; } if (conversionType == typeof(sbyte)) { value = (sbyte) this; return true; } if (conversionType == typeof(float)) { value = (float) this; return true; } if (conversionType == typeof(string)) { value = ToString(null, provider); return true; } if (conversionType == typeof(ushort)) { value = (ushort) this; return true; } if (conversionType == typeof(uint)) { value = (uint) this; return true; } if (conversionType == typeof(ulong)) { value = (ulong) this; return true; } if (conversionType == typeof(byte[])) { value = ToByteArray(); return true; } if (conversionType == typeof(Guid)) { value = new Guid(ToByteArray()); return true; } value = null; return false; } public static Int256 Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static Int256 Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static Int256 Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static Int256 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 Int256 result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out Int256 result) { result = Zero; 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 TryParseHex(value, out result); return TryParseNum(value, out result); } private static bool TryParseHex(string value, out Int256 result) { if (value.Length > 64) throw new OverflowException(); result = Zero; var pos = 0; for (var i = value.Length - 1; i >= 0; i--) { var ch = value[i]; ulong bch; if (ch >= '0' && ch <= '9') bch = (ulong) (ch - '0'); else if (ch >= 'A' && ch <= 'F') bch = (ulong) (ch - 'A' + 10); else if (ch >= 'a' && ch <= 'f') bch = (ulong) (ch - 'a' + 10); else return false; if (pos < 64) result.Bytes0To8 |= bch << pos; else if (pos < 128) result.Bytes8To16 |= bch << pos; else if (pos < 192) result.Bytes16To24 |= bch << pos; else if (pos < 256) result.Bytes24To32 |= bch << pos; pos += 4; } return true; } private static bool TryParseNum(string value, out Int256 result) { result = Zero; foreach (var ch in value) { byte b; if (ch >= '0' && ch <= '9') b = (byte) (ch - '0'); else return false; result = Ten * result; result += b; } return true; } public object ToType(Type conversionType, IFormatProvider provider) { object value; if (TryConvert(conversionType, provider, out value)) return value; throw new InvalidCastException(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { if (Bytes8To16 != 0) throw new OverflowException(); return Convert.ToUInt16(Bytes0To8); } uint IConvertible.ToUInt32(IFormatProvider provider) { if (Bytes8To16 != 0) throw new OverflowException(); return Convert.ToUInt32(Bytes0To8); } ulong IConvertible.ToUInt64(IFormatProvider provider) { if (Bytes8To16 != 0) throw new OverflowException(); return Bytes0To8; } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public static int Compare(Int256 left, object right) { if (right is Int256) return Compare(left, (Int256) right); if (right is bool) return Compare(left, new Int256((bool) right)); if (right is byte) return Compare(left, new Int256((byte) right)); if (right is char) return Compare(left, new Int256((char) right)); if (right is decimal) return Compare(left, new Int256((decimal) right)); if (right is double) return Compare(left, new Int256((double) right)); if (right is short) return Compare(left, new Int256((short) right)); if (right is int) return Compare(left, new Int256((int) right)); if (right is long) return Compare(left, new Int256((long) right)); if (right is sbyte) return Compare(left, new Int256((sbyte) right)); if (right is float) return Compare(left, new Int256((float) right)); if (right is ushort) return Compare(left, new Int256((ushort) right)); if (right is uint) return Compare(left, new Int256((uint) right)); if (right is ulong) return Compare(left, new Int256((ulong) right)); var bytes = right as byte[]; if (bytes != null && bytes.Length != 32) return Compare(left, new Int256(bytes)); if (right is Guid) return Compare(left, new Int256((Guid) right)); throw new ArgumentException(); } public static int Compare(Int256 left, Int256 right) { var leftSign = left.Sign; var rightSign = right.Sign; if (leftSign == 0 && rightSign == 0) return 0; if (leftSign >= 0 && rightSign < 0) return 1; if (leftSign < 0 && rightSign >= 0) return -1; if (left.Bytes24To32 != right.Bytes24To32) return left.Bytes24To32.CompareTo(right.Bytes24To32); if (left.Bytes16To24 != right.Bytes16To24) return left.Bytes16To24.CompareTo(right.Bytes16To24); if (left.Bytes8To16 != right.Bytes8To16) return left.Bytes8To16.CompareTo(right.Bytes8To16); return left.Bytes0To8.CompareTo(right.Bytes0To8); } public int CompareTo(Int256 value) { return Compare(this, value); } public static implicit operator Int256(bool value) { return new Int256(value); } public static implicit operator Int256(byte value) { return new Int256(value); } public static implicit operator Int256(char value) { return new Int256(value); } public static explicit operator Int256(decimal value) { return new Int256(value); } public static explicit operator Int256(double value) { return new Int256(value); } public static implicit operator Int256(short value) { return new Int256(value); } public static implicit operator Int256(int value) { return new Int256(value); } public static implicit operator Int256(long value) { return new Int256(value); } public static implicit operator Int256(sbyte value) { return new Int256(value); } public static explicit operator Int256(float value) { return new Int256(value); } public static implicit operator Int256(ushort value) { return new Int256(value); } public static implicit operator Int256(uint value) { return new Int256(value); } public static implicit operator Int256(ulong value) { return new Int256(value); } public static explicit operator bool(Int256 value) { return value.Sign != 0; } public static explicit operator byte(Int256 value) { if (value.Sign == 0) return 0; if (value.Sign < 0 || value.Bytes0To8 > 0xFF) throw new OverflowException(); return (byte) value.Bytes0To8; } public static explicit operator char(Int256 value) { if (value.Sign == 0) return (char) 0; if (value.Sign < 0 || value.Bytes0To8 > 0xFFFF) throw new OverflowException(); return (char) (ushort) value.Bytes0To8; } public static explicit operator decimal(Int256 value) { return value.Sign == 0 ? 0 : new decimal((int) (value.Bytes0To8 & 0xFFFFFFFF), (int) (value.Bytes8To16 & 0xFFFFFFFF), (int) (value.Bytes16To24 & 0xFFFFFFFF), value.Sign < 0, 0); } public static explicit operator double(Int256 value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!double.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var d)) throw new OverflowException(); return d; } public static explicit operator float(Int256 value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!float.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var f)) throw new OverflowException(); return f; } public static explicit operator short(Int256 value) { if (value.Sign == 0) return 0; if (value.Bytes0To8 > 0x8000) throw new OverflowException(); if (value.Bytes0To8 == 0x8000 && value.Sign > 0) throw new OverflowException(); return (short) ((int) value.Bytes0To8 * value.Sign); } public static explicit operator int(Int256 value) { if (value.Sign == 0) return 0; if (value.Bytes0To8 > 0x80000000) throw new OverflowException(); if (value.Bytes0To8 == 0x80000000 && value.Sign > 0) throw new OverflowException(); return (int) value.Bytes0To8 * value.Sign; } public static explicit operator long(Int256 value) { if (value.Sign == 0) return 0; if (value.Bytes0To8 > long.MaxValue) throw new OverflowException(); return (long) value.Bytes0To8 * value.Sign; } public static explicit operator uint(Int256 value) { if (value.Sign == 0) return 0; if (value.Sign < 0 || value.Bytes0To8 > uint.MaxValue) throw new OverflowException(); return (uint) value.Bytes0To8; } public static explicit operator ushort(Int256 value) { if (value.Sign == 0) return 0; if (value.Sign < 0 || value.Bytes0To8 > ushort.MaxValue) throw new OverflowException(); return (ushort) value.Bytes0To8; } public static explicit operator ulong(Int256 value) { if (value.Sign < 0 || value.Bytes8To16 != 0) throw new OverflowException(); return value.Bytes0To8; } public static bool operator >(Int256 left, Int256 right) { return Compare(left, right) > 0; } public static bool operator <(Int256 left, Int256 right) { return Compare(left, right) < 0; } public static bool operator >=(Int256 left, Int256 right) { return Compare(left, right) >= 0; } public static bool operator <=(Int256 left, Int256 right) { return Compare(left, right) <= 0; } public static bool operator !=(Int256 left, Int256 right) { return Compare(left, right) != 0; } public static bool operator ==(Int256 left, Int256 right) { return Compare(left, right) == 0; } public static Int256 operator +(Int256 value) { return value; } public static Int256 operator ~(Int256 value) { return -(value + One); } public static Int256 operator -(Int256 value) { return Negate(value); } public static Int256 operator ++(Int256 value) { return value + 1; } public static Int256 operator --(Int256 value) { return value - 1; } public static Int256 Negate(Int256 value) { return new Int256(~value.Bytes24To32, ~value.Bytes16To24, ~value.Bytes8To16, ~value.Bytes0To8) + 1; } public Int256 ToAbs() { return Abs(this); } public static Int256 Abs(Int256 value) { if (value.Sign < 0) return -value; return value; } public static Int256 operator +(Int256 left, Int256 right) { left.Bytes24To32 += right.Bytes24To32; left.Bytes16To24 += right.Bytes16To24; if (left.Bytes16To24 < right.Bytes16To24) left.Bytes24To32++; left.Bytes8To16 += right.Bytes8To16; if (left.Bytes8To16 < right.Bytes8To16) { left.Bytes16To24++; if (left.Bytes16To24 < left.Bytes16To24 - 1) left.Bytes24To32++; } left.Bytes0To8 += right.Bytes0To8; if (left.Bytes0To8 < right.Bytes0To8) { left.Bytes8To16++; if (left.Bytes8To16 < left.Bytes8To16 - 1) { left.Bytes16To24++; if (left.Bytes16To24 < left.Bytes16To24 - 1) left.Bytes24To32++; } } return left; } public static Int256 operator -(Int256 left, Int256 right) { return left + -right; } public static Int256 Add(Int256 left, Int256 right) { return left + right; } public static Int256 Subtract(Int256 left, Int256 right) { return left - right; } public static Int256 Divide(Int256 dividend, Int256 divisor) { return DivRem(dividend, divisor, out var integer); } public static Int256 DivRem(Int256 dividend, Int256 divisor, out Int256 remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quotient, out var rem); remainder = new Int256(1, rem); return new Int256(dividend.Sign * divisor.Sign, quotient); } private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder) { const ulong hiBit = 0x100000000; var divisorLen = GetLength(divisor); var dividendLen = GetLength(dividend); if (divisorLen <= 1) { ulong rem = 0; var div = divisor[0]; quotient = new uint[dividendLen]; remainder = new uint[1]; for (var i = dividendLen - 1; i >= 0; i--) { rem *= hiBit; rem += dividend[i]; var q = rem / div; rem -= q * div; quotient[i] = (uint) q; } remainder[0] = (uint) rem; return; } if (dividendLen >= divisorLen) { var shift = GetNormalizeShift(divisor[divisorLen - 1]); var normDividend = new uint[dividendLen + 1]; var normDivisor = new uint[divisorLen]; Normalize(dividend, dividendLen, normDividend, shift); Normalize(divisor, divisorLen, normDivisor, shift); quotient = new uint[dividendLen - divisorLen + 1]; for (var j = dividendLen - divisorLen; j >= 0; j--) { var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1]; var qj = dx / normDivisor[divisorLen - 1]; dx -= qj * normDivisor[divisorLen - 1]; do { if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2]) break; qj -= 1L; dx += normDivisor[divisorLen - 1]; } while (dx < hiBit); long di = 0; long dj; var index = 0; while (index < divisorLen) { var dqj = normDivisor[index] * qj; dj = normDividend[index + j] - (uint) dqj - di; normDividend[index + j] = (uint) dj; dqj = dqj >> 32; dj = dj >> 32; di = (long) dqj - dj; index++; } dj = normDividend[j + divisorLen] - di; normDividend[j + divisorLen] = (uint) dj; quotient[j] = (uint) qj; if (dj < 0) { quotient[j]--; ulong sum = 0; for (index = 0; index < divisorLen; index++) { sum = normDivisor[index] + normDividend[j + index] + sum; normDividend[j + index] = (uint) sum; sum = sum >> 32; } sum += normDividend[j + divisorLen]; normDividend[j + divisorLen] = (uint) sum; } } remainder = Unnormalize(normDividend, shift); return; } quotient = new uint[0]; remainder = dividend; } private static int GetLength(uint[] uints) { var index = uints.Length - 1; while (index >= 0 && uints[index] == 0) index--; return index + 1; } private static int GetNormalizeShift(uint ui) { var shift = 0; if ((ui & 0xffff0000) == 0) { ui = ui << 16; shift += 16; } if ((ui & 0xff000000) == 0) { ui = ui << 8; shift += 8; } if ((ui & 0xf0000000) == 0) { ui = ui << 4; shift += 4; } if ((ui & 0xc0000000) == 0) { ui = ui << 2; shift += 2; } if ((ui & 0x80000000) == 0) shift++; return shift; } private static uint[] Unnormalize(uint[] normalized, int shift) { var len = GetLength(normalized); var unnormalized = new uint[len]; if (shift > 0) { var rshift = 32 - shift; uint r = 0; for (var i = len - 1; i >= 0; i--) { unnormalized[i] = (normalized[i] >> shift) | r; r = normalized[i] << rshift; } } else { for (var j = 0; j < len; j++) unnormalized[j] = normalized[j]; } return unnormalized; } private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift) { int i; uint n = 0; if (shift > 0) { var rShift = 32 - shift; for (i = 0; i < len; i++) { normalized[i] = (unormalized[i] << shift) | n; n = unormalized[i] >> rShift; } } else { i = 0; while (i < len) { normalized[i] = unormalized[i]; i++; } } while (i < normalized.Length) normalized[i++] = 0; if (n != 0) normalized[len] = n; } public static Int256 Remainder(Int256 dividend, Int256 divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static Int256 Max(Int256 left, Int256 right) { return left.CompareTo(right) < 0 ? right : left; } public static Int256 Min(Int256 left, Int256 right) { return left.CompareTo(right) <= 0 ? left : right; } public static int GetBitWidth(Int256 n) { Int256 bitWidth = 1; var v = n; while ((v >>= 1) > 0) bitWidth++; if (bitWidth < 8) bitWidth = 8; while (bitWidth % 8 != 0) bitWidth++; return (int) bitWidth; } public static Int256 operator %(Int256 dividend, Int256 divisor) { return Remainder(dividend, divisor); } public static Int256 operator /(Int256 dividend, Int256 divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { return new[] {Bytes0To8, Bytes8To16, Bytes16To24, Bytes24To32}; } public uint[] ToUIn32Array() { var uia = new uint[8]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, uia, 0, 32); return uia; } public byte[] ToByteArray() { var ba = new byte[32]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, ba, 0, 32); return ba; } public static Int256 Multiply(Int256 left, Int256 right) { var xInts = left.ToUIn32Array(); var yInts = right.ToUIn32Array(); var mulInts = new uint[16]; for (var i = 0; i < xInts.Length; i++) { var index = i; ulong remainder = 0; foreach (var yi in yInts) { remainder = remainder + (ulong) xInts[i] * yi + mulInts[index]; mulInts[index++] = (uint) remainder; remainder = remainder >> 32; } while (remainder != 0) { remainder += mulInts[index]; mulInts[index++] = (uint) remainder; remainder = remainder >> 32; } } return new Int256(left.Sign * right.Sign, mulInts); } public static Int256 operator *(Int256 left, Int256 right) { return Multiply(left, right); } public static Int256 operator >>(Int256 value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value << int.MaxValue << 1; if (shift < 0) return value << -shift; var digitShift = shift / 32; var smallShift = shift - digitShift * 32; var xd = value.ToUIn32Array(); var xl = xd.Length; if (value.Sign < 0) { if (shift >= 32 * xl) return new Int256(-1); var zd = new uint[xl]; Array.Copy(xd, zd, xl); xd = zd; TwosComplement(xd); } var length = xl - digitShift; if (length < 0) length = 0; var d = new uint[length]; if (smallShift == 0) { for (var index = xl - 1; index >= digitShift; --index) d[index - digitShift] = xd[index]; } else { var carryShift = 32 - smallShift; uint carry = 0; for (var index = xl - 1; index >= digitShift; --index) { var rot = xd[index]; d[index - digitShift] = !(value.Sign < 0) || index != xl - 1 ? (rot >> smallShift) | carry : (rot >> smallShift) | (uint) (-1 << carryShift); carry = rot << carryShift; } } if (value.Sign < 0) TwosComplement(d); return new Int256(value.Sign, d); } private static void TwosComplement(uint[] d) { uint v = 0; var i = 0; for (; i < d.Length; i++) { v = ~d[i] + 1; d[i] = v; if (v != 0) { i++; break; } } if (v != 0) { for (; i < d.Length; i++) d[i] = ~d[i]; } else { var len = d.Length + 1; var r = new uint[len]; var n = Math.Min(d.Length, len); for (var j = 0; j < n; j++) r[j] = d[j]; d = r; d[d.Length - 1] = 1; } } public static Int256 operator <<(Int256 value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value >> int.MaxValue >> 1; if (shift < 0) return value >> -shift; var digitShift = shift / 32; var smallShift = shift - digitShift * 32; var xd = value.ToUIn32Array(); var xl = xd.Length; var zd = new uint[xl + digitShift + 1]; if (smallShift == 0) { for (var index = 0; index < xl; ++index) zd[index + digitShift] = xd[index]; } else { var carryShift = 32 - smallShift; uint carry = 0; int index; for (index = 0; index < xl; ++index) { var rot = xd[index]; zd[index + digitShift] = (rot << smallShift) | carry; carry = rot >> carryShift; } zd[index + digitShift] = carry; } return new Int256(value.Sign, zd); } public static Int256 operator |(Int256 left, Int256 right) { if (left == 0) return right; if (right == 0) return left; var x = left.ToUIn32Array(); var y = right.ToUIn32Array(); var z = new uint[Math.Max(x.Length, y.Length)]; var xExtend = left.Sign < 0 ? uint.MaxValue : 0; var yExtend = right.Sign < 0 ? uint.MaxValue : 0; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : xExtend; var yu = i < y.Length ? y[i] : yExtend; z[i] = xu | yu; } return new Int256(left.Sign * right.Sign, z); } public static Int256 operator ^(Int256 left, Int256 right) { var x = left.ToUIn32Array(); var y = right.ToUIn32Array(); var z = new uint[Math.Max(x.Length, y.Length)]; var xExtend = left.Sign < 0 ? uint.MaxValue : 0; var yExtend = right.Sign < 0 ? uint.MaxValue : 0; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : xExtend; var yu = i < y.Length ? y[i] : yExtend; z[i] = xu ^ yu; } return new Int256(left.Sign * right.Sign, z); } public static Int256 operator &(Int256 left, Int256 right) { if (left == 0 || right == 0) return Zero; var x = left.ToUIn32Array(); var y = right.ToUIn32Array(); var z = new uint[Math.Max(x.Length, y.Length)]; var xExtend = left.Sign < 0 ? uint.MaxValue : 0; var yExtend = right.Sign < 0 ? uint.MaxValue : 0; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : xExtend; var yu = i < y.Length ? y[i] : yExtend; z[i] = xu & yu; } return new Int256(left.Sign * right.Sign, z); } public class Int256Converter : 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 Int256(); } 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); } } }