Int512 Bit Class
Jun-11,2021: Obsolete Use xIntX Instead.
using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(Int512Converter))] [DebuggerDisplay("{DDisplay}")] public struct Int512 : IComparable<Int512>, IComparable, IEquatable<Int512>, IConvertible, IFormattable { private ulong B512; private ulong B448; private ulong B384; private ulong B320; private ulong B256; private ulong B192; private ulong B128; private ulong B64; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public static Int512 Zero = new Int512(0); public static Int512 Ten = new Int512(10); public static Int512 One = new Int512(1); public static Int512 MaxValue = GetMaxValue(); public static Int512 MinValue = GetMinValue(); private const ulong HiNeg = 0x8000000000000000; private static Int512 GetMaxValue() { return new Int512(long.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue); } private static Int512 GetMinValue() { return -GetMaxValue(); } public Int512(Int512 value) { B512 = value.B512; B448 = value.B448; B384 = value.B384; B320 = value.B320; B256 = value.B256; B192 = value.B192; B128 = value.B128; B64 = value.B64; } public Int512(string value) { if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); B512 = result.B512; B448 = result.B448; B384 = result.B384; B320 = result.B320; B256 = result.B256; B192 = result.B192; B128 = result.B128; B64 = result.B64; } public Int512(byte value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = value; } public Int512(bool value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = (ulong) (value ? 1 : 0); } public Int512(char value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = value; } public Int512(BigDecimal value) { var ba = value.UnscaledValue.ToByteArray(); B512 = BitConverter.ToUInt64(ba, 56); B448 = BitConverter.ToUInt64(ba, 48); B384 = BitConverter.ToUInt64(ba, 40); B320 = BitConverter.ToUInt64(ba, 32); B256 = BitConverter.ToUInt64(ba, 24); B192 = BitConverter.ToUInt64(ba, 16); B128 = BitConverter.ToUInt64(ba, 8); B64 = BitConverter.ToUInt64(ba, 0); } public Int512(decimal value) { if (value < 0) { var n = -new Int512(-value); B512 = n.B512; B448 = n.B448; B384 = n.B384; B320 = n.B320; B256 = n.B256; B192 = n.B192; B128 = n.B128; B64 = n.B64; return; } var bits = decimal.GetBits(value); B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = (uint) bits[2]; B128 = (uint) bits[1]; B64 = (uint) bits[0]; } public Int512(double value) : this((decimal) value) { } public Int512(float value) : this((decimal) value) { } public Int512(short value) { if (value < 0) { var n = -new Int512(-(value + 1)) - 1; B512 = n.B512; B448 = n.B448; B384 = n.B384; B320 = n.B320; B256 = n.B256; B192 = n.B192; B128 = n.B128; B64 = n.B64; return; } B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = (ulong) value; } public Int512(int value) { if (value < 0) { var n = -new Int512(-(value + 1)) - 1; B512 = n.B512; B448 = n.B448; B384 = n.B384; B320 = n.B320; B256 = n.B256; B192 = n.B192; B128 = n.B128; B64 = n.B64; return; } B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = (ulong) value; } public Int512(long value) { if (value < 0) { var n = -new Int512(-(value + 1)) - 1; B512 = n.B512; B448 = n.B448; B384 = n.B384; B320 = n.B320; B256 = n.B256; B192 = n.B192; B128 = n.B128; B64 = n.B64; return; } B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = (ulong) value; } public Int512(sbyte value) { if (value < 0) { var n = -new Int512(-(value + 1)) - 1; B512 = n.B512; B448 = n.B448; B384 = n.B384; B320 = n.B320; B256 = n.B256; B192 = n.B192; B128 = n.B128; B64 = n.B64; return; } B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = (ulong) value; } public Int512(ushort value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = value; } public Int512(uint value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = value; } public Int512(ulong value) { B512 = 0; B448 = 0; B384 = 0; B320 = 0; B256 = 0; B192 = 0; B128 = 0; B64 = value; } public Int512(BigInteger value) : this(value.ToByteArray()) { var aba = MaxValue.ToByteArray(); var bn = new BigInteger(aba); } public Int512(Guid value) : this(value.ToByteArray()) { } public Int512(byte[] value) { if (value == null) throw new Exception("Value cannot be null."); if (value.Length != 64) Array.Resize(ref value, 64); B512 = BitConverter.ToUInt64(value, 56); B448 = BitConverter.ToUInt64(value, 48); B384 = BitConverter.ToUInt64(value, 40); B320 = BitConverter.ToUInt64(value, 32); B256 = BitConverter.ToUInt64(value, 24); B192 = BitConverter.ToUInt64(value, 16); B128 = BitConverter.ToUInt64(value, 8); B64 = BitConverter.ToUInt64(value, 0); } public Int512(ulong b512, ulong b448, ulong b384, ulong b320, ulong b256, ulong b192, ulong b128, ulong b64) { B512 = b512; B448 = b448; B384 = b384; B320 = b320; B256 = b256; B192 = b192; B128 = b128; B64 = b64; } public Int512(int sign, uint[] array) { if (array == null) throw new Exception("Array cannot be null."); var b512 = new byte[8]; var b448 = new byte[8]; var b384 = new byte[8]; var b320 = new byte[8]; var b256 = new byte[8]; var b192 = new byte[8]; var b128 = new byte[8]; var b64 = new byte[8]; if (array.Length > 0) { Array.Copy(BitConverter.GetBytes(array[0]), 0, b64, 0, 4); if (array.Length > 1) { Array.Copy(BitConverter.GetBytes(array[1]), 0, b64, 4, 4); if (array.Length > 2) { Array.Copy(BitConverter.GetBytes(array[2]), 0, b128, 0, 4); if (array.Length > 3) { Array.Copy(BitConverter.GetBytes(array[3]), 0, b128, 4, 4); if (array.Length > 4) { Array.Copy(BitConverter.GetBytes(array[4]), 0, b192, 0, 4); if (array.Length > 5) { Array.Copy(BitConverter.GetBytes(array[5]), 0, b192, 4, 4); if (array.Length > 6) { Array.Copy(BitConverter.GetBytes(array[6]), 0, b256, 0, 4); if (array.Length > 7) { Array.Copy(BitConverter.GetBytes(array[7]), 0, b256, 4, 4); if (array.Length > 8) { Array.Copy(BitConverter.GetBytes(array[8]), 0, b320, 0, 4); if (array.Length > 9) { Array.Copy(BitConverter.GetBytes(array[9]), 0, b320, 4, 4); if (array.Length > 10) { Array.Copy(BitConverter.GetBytes(array[10]), 0, b384, 0, 4); if (array.Length > 11) { Array.Copy(BitConverter.GetBytes(array[11]), 0, b384, 4, 4); if (array.Length > 12) { Array.Copy(BitConverter.GetBytes(array[12]), 0, b448, 0, 4); if (array.Length > 13) { Array.Copy(BitConverter.GetBytes(array[13]), 0, b448, 4, 4); if (array.Length > 14) { Array.Copy(BitConverter.GetBytes(array[14]), 0, b512, 0, 4); if (array.Length > 15) Array.Copy(BitConverter.GetBytes(array[15]), 0, b512, 4, 4); } } } } } } } } } } } } } } } B512 = BitConverter.ToUInt64(b512, 0); B448 = BitConverter.ToUInt64(b448, 0); B384 = BitConverter.ToUInt64(b384, 0); B320 = BitConverter.ToUInt64(b320, 0); B256 = BitConverter.ToUInt64(b256, 0); B192 = BitConverter.ToUInt64(b192, 0); B128 = BitConverter.ToUInt64(b128, 0); B64 = BitConverter.ToUInt64(b64, 0); if (sign < 0) B512 |= HiNeg; else B512 &= ~HiNeg; } public int BitWidth { get { Int512 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 { var allZero = true; var ba = ToUIn32Array(); for (var i = 0; i < ba.Length; i++) if (ba[i] != 0) { allZero = false; break; } if (allZero) return 0; return (B512 & HiNeg) == 0 ? 1 : -1; } } public bool IsOne => this == One; public bool IsEven => this % 2 == 0; public bool IsNegative => Sign < 0; public bool IsZero => B512 == 0 && B448 == 0 && B384 == 0 && B320 == 0 && B256 == 0 && B192 == 0 && B128 == 0 && B64 == 0; public override int GetHashCode() { return B64.GetHashCode() ^ B128.GetHashCode() ^ B192.GetHashCode() ^ B256.GetHashCode() ^ B320.GetHashCode() ^ B384.GetHashCode() ^ B448.GetHashCode() ^ B512.GetHashCode(); } public override bool Equals(object obj) { return base.Equals(obj); } public bool Equals(Int512 obj) { return B512 == obj.B512 && B448 == obj.B448 && B384 == obj.B384 && B320 == obj.B320 && B256 == obj.B256 && B192 == obj.B192 && B128 == obj.B128 && B64 == obj.B64; } 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)), 10); } 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, int radix) { if (radix < 2 || radix > 36) throw new ArgumentOutOfRangeException("radix"); if (Sign == 0) return "0"; var negative = Sign < 0; var a = negative ? Abs(this) : this; var biRadix = new Int512(radix); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; var al = new ArrayList(); while (a.Sign != 0 && a.B64 != 0) { Divide(a, biRadix, out var remainder, out var quotient); al.Insert(0, charSet[(int) remainder.B64]); a = quotient; } var result = new string((char[]) al.ToArray(typeof(char))); if (radix == 10 && negative) return "-" + result; return result; } public static Int512 Abs(Int512 value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.Sign < 0) return -value; return value; } 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 Int512 Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static Int512 Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static Int512 Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static Int512 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 Int512 result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out Int512 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 TryParseNum(value, 16, out result); return TryParseNum(value, 10, out result); } public static bool TryParseNum(string digits, int radix, out Int512 result) { result = new Int512(); if (digits == null) return false; var multiplier = new Int512(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 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 (B128 != 0) throw new OverflowException(); return Convert.ToUInt16(B64); } uint IConvertible.ToUInt32(IFormatProvider provider) { if (B128 != 0) throw new OverflowException(); return Convert.ToUInt32(B64); } ulong IConvertible.ToUInt64(IFormatProvider provider) { if (B128 != 0) throw new OverflowException(); return B64; } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public static int Compare(Int512 left, object right) { if (right is Int512) return Compare(left, (Int512) right); if (right is bool) return Compare(left, new Int512((bool) right)); if (right is byte) return Compare(left, new Int512((byte) right)); if (right is char) return Compare(left, new Int512((char) right)); if (right is decimal) return Compare(left, new Int512((decimal) right)); if (right is double) return Compare(left, new Int512((double) right)); if (right is short) return Compare(left, new Int512((short) right)); if (right is int) return Compare(left, new Int512((int) right)); if (right is long) return Compare(left, new Int512((long) right)); if (right is sbyte) return Compare(left, new Int512((sbyte) right)); if (right is float) return Compare(left, new Int512((float) right)); if (right is ushort) return Compare(left, new Int512((ushort) right)); if (right is uint) return Compare(left, new Int512((uint) right)); if (right is ulong) return Compare(left, new Int512((ulong) right)); var bytes = right as byte[]; if (bytes != null && bytes.Length != 64) return Compare(left, new Int512(bytes)); if (right is Guid) return Compare(left, new Int512((Guid) right)); throw new ArgumentException(); } public static int Compare(Int512 left, Int512 right) { if (ReferenceEquals(left, right)) return 0; if (ReferenceEquals(left, null)) throw new ArgumentNullException("leftSide"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("rightSide"); if (left > right) return 1; if (left == right) return 0; return -1; } public int CompareTo(Int512 value) { return Compare(this, value); } public static implicit operator Int512(bool value) { return new Int512(value); } public static implicit operator Int512(byte value) { return new Int512(value); } public static implicit operator Int512(char value) { return new Int512(value); } public static explicit operator Int512(decimal value) { return new Int512(value); } public static explicit operator Int512(double value) { return new Int512(value); } public static implicit operator Int512(short value) { return new Int512(value); } public static implicit operator Int512(int value) { return new Int512(value); } public static implicit operator Int512(long value) { return new Int512(value); } public static implicit operator Int512(sbyte value) { return new Int512(value); } public static explicit operator Int512(float value) { return new Int512(value); } public static implicit operator Int512(ushort value) { return new Int512(value); } public static implicit operator Int512(uint value) { return new Int512(value); } public static implicit operator Int512(ulong value) { return new Int512(value); } public static implicit operator Int512(BigInteger value) { return new Int512(value); } public static implicit operator Int512(BigDecimal value) { return new Int512(value); } public static explicit operator bool(Int512 value) { return (byte) value.B64 != 0; } public static explicit operator byte(Int512 value) { return (byte) value.B64; } public static explicit operator char(Int512 value) { return (char) (ushort) value.B64; } public static explicit operator decimal(Int512 value) { if (value.Sign == 0) return 0; return new decimal((int) (value.B64 & 0xFFFFFFFF), (int) (value.B64 >> 32), (int) (value.B128 & 0xFFFFFFFF), value.Sign < 0, 0); } public static explicit operator double(Int512 value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d)) throw new OverflowException(); return d; } public static explicit operator float(Int512 value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f)) throw new OverflowException(); return f; } public static explicit operator short(Int512 value) { if (value.B64 > 0x8000) throw new OverflowException(); if (value.B64 == 0x8000 && value.Sign > 0) throw new OverflowException(); return (short) ((int) value.B64 * value.Sign); } public static explicit operator int(Int512 value) { if (value.Sign == 0) return 0; if (value.B64 > 0x80000000) throw new OverflowException(); if (value.B64 == 0x80000000 && value.Sign > 0) throw new OverflowException(); return (int) value.B64 * value.Sign; } public static explicit operator long(Int512 value) { if (value.Sign == 0) return 0; if (value.B64 > long.MaxValue) throw new OverflowException(); return (long) value.B64 * value.Sign; } public static explicit operator uint(Int512 value) { if (value.Sign == 0) return 0; if (value.Sign < 0 || value.B64 > uint.MaxValue) throw new OverflowException(); return (uint) value.B64; } public static explicit operator ushort(Int512 value) { if (value.Sign == 0) return 0; if (value.Sign < 0 || value.B64 > ushort.MaxValue) throw new OverflowException(); return (ushort) value.B64; } public static explicit operator ulong(Int512 value) { if (value.Sign < 0 || value.B64 != 0) throw new OverflowException(); return value.B64; } public static explicit operator BigInteger(Int512 value) { return new BigInteger(value.ToByteArray()); } public static bool operator >(Int512 left, Int512 right) { if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); if (left.Sign != right.Sign) return right.Sign < 0; if (left.B64 != right.B64) return left.B64 > right.B64; if (left.B128 != right.B128) return left.B128 > right.B128; if (left.B192 != right.B192) return left.B192 > right.B192; if (left.B256 != right.B256) return left.B256 > right.B256; if (left.B320 != right.B320) return left.B320 > right.B320; if (left.B384 != right.B384) return left.B384 > right.B384; if (left.B448 != right.B448) return left.B448 > right.B448; if (left.B512 != right.B512) return left.B512 > right.B512; return false; } public static bool operator <(Int512 left, Int512 right) { return Compare(left, right) < 0; } public static bool operator >=(Int512 left, Int512 right) { return Compare(left, right) >= 0; } public static bool operator <=(Int512 left, Int512 right) { return Compare(left, right) <= 0; } public static bool operator !=(Int512 left, Int512 right) { return Compare(left, right) != 0; } public static bool operator ==(Int512 left, Int512 right) { if (ReferenceEquals(left, right)) return true; if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) return false; if (left.Sign != right.Sign) return false; return left.Equals(right); } public static Int512 operator +(Int512 value) { return value; } public static Int512 operator ~(Int512 value) { return -(value + One); } public static Int512 operator -(Int512 value) { return Negate(value); } public static Int512 operator ++(Int512 value) { return value + 1; } public static Int512 operator --(Int512 value) { return value - 1; } public static Int512 Negate(Int512 value) { return new Int512(~value.B512, ~value.B448, ~value.B384, ~value.B320, ~value.B256, ~value.B192, ~value.B128, ~value.B64) + 1; } public static Int512 operator +(Int512 left, Int512 right) { var larr = left.ToUIn32Array(); var rarr = right.ToUIn32Array(); var dl = larr.Length > rarr.Length ? larr.Length : rarr.Length; var result = new uint[dl]; long carry = 0; for (var i = 0; i < dl; i++) { var sum = larr[i] + (long) rarr[i] + carry; carry = sum >> 32; result[i] = (uint) (sum & 0xFFFFFFFF); } if (carry != 0) { var idx = 0; while (idx< result.Length-1) { if (result[idx] == 0) break; idx++; } result[idx] = (uint) carry; } return new Int512(left.Sign * right.Sign, result); } public static Int512 operator -(Int512 left, Int512 right) { return left + -right; } public static Int512 Add(Int512 left, Int512 right) { return left + right; } public static Int512 Subtract(Int512 left, Int512 right) { return left - right; } public static Int512 Divide(Int512 dividend, Int512 divisor) { return DivRem(dividend, divisor, out var integer); } public static void Divide(Int512 dividend, Int512 divisor, out Int512 remainder, out Int512 quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quo, out var rem); remainder = new Int512(1, rem); quotient = new Int512(dividend.Sign * divisor.Sign, quo); } public static Int512 DivRem(Int512 dividend, Int512 divisor, out Int512 remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quotient, out var rem); remainder = new Int512(1, rem); return new Int512(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); ulong di = 0; ulong 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 = dqj - dj; index++; } dj = normDividend[j + divisorLen] - di; normDividend[j + divisorLen] = (uint) dj; quotient[j] = (uint) qj; if ((long)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 Int512 Remainder(Int512 dividend, Int512 divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static Int512 Max(Int512 left, Int512 right) { return left.CompareTo(right) < 0 ? right : left; } public static Int512 Min(Int512 left, Int512 right) { return left.CompareTo(right) <= 0 ? left : right; } public static int GetBitWidth(Int512 n) { Int512 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 Int512 operator %(Int512 dividend, Int512 divisor) { return Remainder(dividend, divisor); } public static Int512 operator /(Int512 dividend, Int512 divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { return new[] {B64, B128, B192, B256, B320, B384, B448, B512}; } public uint[] ToUIn32Array() { var uia = new uint[16]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, uia, 0, 64); return uia; } public byte[] ToByteArray() { var ba = new byte[64]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, ba, 0, 64); return ba; } public static Int512 Multiply(Int512 left, Int512 right) { var xInts = left.ToUIn32Array(); var yInts = right.ToUIn32Array(); var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1]; 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 Int512(left.Sign * right.Sign, mulInts); } public static Int512 operator *(Int512 left, Int512 right) { return Multiply(left, right); } public static Int512 operator >>(Int512 value, int shift) { var values = value.ToUIn64Array(); var valueLength = sizeof(ulong) * 8; var length = values.Length; shift = shift % (length * valueLength); var shiftOffset = shift / valueLength; var bshift = shift % valueLength; var shifted = new ulong[length]; for (var i = 0; i < length; i++) { var ishift = i - shiftOffset; if (ishift < 0) continue; shifted[ishift] |= values[i] >> bshift; if (bshift > 0 && i + 1 < length) shifted[ishift] |= values[i + 1] << (valueLength - bshift); } return new Int512(shifted[7], shifted[6], shifted[5], shifted[4], shifted[3], shifted[2], shifted[1], shifted[0]); } public static Int512 operator <<(Int512 value, int shift) { var values = value.ToUIn64Array(); var valueLength = sizeof(ulong) * 8; var length = values.Length; shift %= length * valueLength; var shiftOffset = shift / valueLength; var bshift = shift % valueLength; var shifted = new ulong[length]; for (var i = 0; i < length; i++) { var ishift = i + shiftOffset; if (ishift >= length) continue; shifted[ishift] |= values[i] << bshift; if (bshift > 0 && i - 1 >= 0) shifted[ishift] |= values[i - 1] >> (valueLength - bshift); } return new Int512(shifted[7], shifted[6], shifted[5], shifted[4], shifted[3], shifted[2], shifted[1], shifted[0]); } public static Int512 operator |(Int512 left, Int512 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)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu | yu; } return new Int512(left.Sign * right.Sign, z); } public static Int512 operator ^(Int512 left, Int512 right) { var x = left.ToUIn32Array(); var y = right.ToUIn32Array(); var z = new uint[Math.Max(x.Length, y.Length)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu ^ yu; } return new Int512(left.Sign * right.Sign, z); } public static Int512 operator &(Int512 left, Int512 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)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu & yu; } return new Int512(left.Sign * right.Sign, z); } private class Int512Converter : 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 Int512(); } 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); } } }