Adjustable Bit Width Big Unsigned or Signed Integer 32,64,128,256,512,1024,2048…
Updated: Jun-11,2021
using System; using System.Collections; using System.Collections.Generic; 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(xIntXConverter))] [DebuggerDisplay("{DDisplay}")] public struct xIntX : IComparable<xIntX>, IComparable, IEquatable<xIntX>, IConvertible, IFormattable { private const int DefaultDataBitWidth = 1024; private const int DataSize = sizeof(uint); private const uint AllBits = ~(uint)0; private const int DataSizeBits = sizeof(uint) * 8; private const uint HiNeg = (uint)1 << (DataSizeBits - 1); private static int _dataBitWidth; private static int DataLength; private static bool _unsigned; public uint[] Data; public xIntX(xIntX value, int bitLength = 0, bool unsigned = false) { if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; _unsigned = unsigned; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; value.Data.CopyTo(Data, 0); } public xIntX(string value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); Data = new uint[DataLength]; result.Data.CopyTo(Data, 0); } public xIntX(byte value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(bool value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = (uint)(value ? 1 : 0); } public xIntX(char value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(BigDecimal value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var ba = value.WholePart.ToByteArray(); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var len = ba.Length / DataSize; var lim = Math.Min(len, DataLength); Data = new uint[lim]; for (var i = 0; i < lim; i++) Data[i] = BitConverter.ToUInt32(ba, i * DataSize); } public xIntX(BigRational value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var v1 = value.Numerator.ToByteArray(); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = v1.Length; var isNegative = _unsigned == false && byteCount > 0 && (v1[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= v1[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= v1[curByte]; } } ConstructFromArray(value.Numerator.ToByteArray(), bitLength); } public xIntX(decimal value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-value, DataBitWidth, _unsigned); n.Data.CopyTo(Data, 0); return; } var bits = decimal.GetBits(value); Data[2] = (uint)bits[2]; Data[1] = (uint)bits[1]; Data[0] = (uint)bits[0]; } public xIntX(double value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned) { } public xIntX(float value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned) { } public xIntX(short value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(int value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(long value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[1] = (uint)((value >> 32) & 0xffffffff); Data[0] = (uint)(value & 0xffffffff); } public xIntX(sbyte value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(ushort value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(uint value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(ulong value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[1] = (uint)((value >> 32) & 0xffffffff); Data[0] = (uint)(value & 0xffffffff); } public xIntX(BigInteger value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned) { } public xIntX(Guid value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned) { } public xIntX(byte[] value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } public xIntX(int sign, uint[] array, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (array == null) throw new Exception("Array cannot be null."); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Math.Min(DataLength, array.Length); i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } if (!_unsigned) { if (sign < 0) Data[DataLength - 1] |= HiNeg; else Data[DataLength - 1] &= ~HiNeg; } } public xIntX(uint[] array, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (array == null) throw new Exception("Array cannot be null."); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (array.Length != DataLength) Array.Resize(ref array, DataLength); Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Data.Length; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } } public bool Unsigned { get => _unsigned; set => _unsigned = value; } private static int DataBitWidth { get => _dataBitWidth; set { _dataBitWidth = value; if (_dataBitWidth < 32) _dataBitWidth = 32; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public xIntX MaxValue { get { var r = new xIntX(0, DataBitWidth, _unsigned); for (var i = 0; i < r.Data.Length; ++i) r.Data[i] = uint.MaxValue; r.Data[r.Data.Length - 1] = int.MaxValue; return r; } } public int BitWidth { get { xIntX bw = 1; var v = new xIntX(this, DataBitWidth, _unsigned); while ((v >>= 1) > 0) bw++; if (bw < 8) bw = 8; while (bw % 8 != 0) bw++; return (int)bw; } } public float MaxDecimalPlaces => DataBitWidth / 64f * 20f; public int DecimalPlaces { get { var a = new xIntX(this, DataBitWidth, _unsigned); var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new xIntX(10, DataBitWidth, _unsigned); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } } public int Sign { get { if (_unsigned) return 1; var allZero = true; var ba = Data; for (var i = 0; i < ba.Length; i++) if (ba[i] != 0) { allZero = false; break; } if (allZero) return 0; return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1; } } public bool IsOne => this == 1; public bool IsEven => (this & 1) == 0; public bool IsNegative => Sign < 0; public bool IsZero { get { for (var i = 0; i < Data.Length; i++) if (Data[i] != 0) return false; return true; } } public int DataUsed { get { var DataUsed = Data.Length; if (!IsNegative) { while (DataUsed > 1 && Data[DataUsed - 1] == 0) --DataUsed; if (DataUsed == 0) DataUsed = 1; } return DataUsed; } } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public int CompareTo(xIntX value) { return Compare(this, 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 (long)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 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 (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt16(Data[0]); } uint IConvertible.ToUInt32(IFormatProvider provider) { if (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt32(Data[0]); } ulong IConvertible.ToUInt64(IFormatProvider provider) { if (Data[1] != 0) return ((ulong)Data[1] << 32) | Data[0]; return Data[0]; } public bool Equals(xIntX obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; if (Data.Length != obj.Data.Length) return false; if (Sign != obj.Sign) return false; for (var i = 0; i < Data.Length; i++) if (Data[i] != obj.Data[i]) 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') { int.TryParse(format.Substring(1).Trim(), out var min); return ToHexString(ch == 'X'); } 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 static byte[] ToByteArray(ulong[] value) { var ba = new byte[value.Length << 3]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3); return ba; } private static byte[] ToByteArray(uint[] value) { var ba = new byte[value.Length << 2]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2); return ba; } public override int GetHashCode() { static uint CombineHash(uint u1, uint u2) { return ((u1 << 7) | (u1 >> 25)) ^ u2; } var s = Sign; var i = Data.Length; while (--i >= 0) s = (int)CombineHash((uint)s, Data[i]); return s; } public static byte[] GetBytesInt(xIntX value) { var b = value.Sign.GetBytes(); var tb = b.Add(value.Data.GetBytes()); return tb; } public override bool Equals(object obj) { return base.Equals(obj); } public override string ToString() { return ToString(null, null); } public string ToString(string format) { return ToString(format, null); } public string ToHexString(bool caps) { 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 = new xIntX(this, DataBitWidth, _unsigned); if (negative) try { a = -a; } catch (Exception ex) { } var biRadix = new xIntX(radix, DataBitWidth, _unsigned); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; var al = new ArrayList(); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); al.Insert(0, charSet[(int)remainder.Data[0]]); a = quotient; } catch (Exception ex) { break; } var result = new string((char[])al.ToArray(typeof(char))); if (radix == 10 && negative) return "-" + result; return result; } public static xIntX Abs(xIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.Sign < 0) return -value; return value; } 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 xIntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static xIntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static xIntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static xIntX 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 xIntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out xIntX result) { result = 0; 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 xIntX result) { result = new xIntX(0, DataBitWidth, _unsigned); if (digits == null) return false; var multiplier = new xIntX(1, DataBitWidth * 2, _unsigned); 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 != 48) { var a = 1; } 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 (multiplier.DataUsed > DataLength) throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} "); } if (digits[0] == '-' && !_unsigned) result = -result; return true; } public static int Compare(xIntX left, object right) { if (right is xIntX) return Compare(left, (xIntX)right); if (right is bool) return Compare(left, new xIntX((bool)right, DataBitWidth, _unsigned)); if (right is byte) return Compare(left, new xIntX((byte)right, DataBitWidth, _unsigned)); if (right is char) return Compare(left, new xIntX((char)right, DataBitWidth, _unsigned)); if (right is decimal) return Compare(left, new xIntX((decimal)right, DataBitWidth, _unsigned)); if (right is double) return Compare(left, new xIntX((double)right, DataBitWidth, _unsigned)); if (right is short) return Compare(left, new xIntX((short)right, DataBitWidth, _unsigned)); if (right is int) return Compare(left, new xIntX((int)right, DataBitWidth, _unsigned)); if (right is long) return Compare(left, new xIntX((long)right, DataBitWidth, _unsigned)); if (right is sbyte) return Compare(left, new xIntX((sbyte)right, DataBitWidth, _unsigned)); if (right is float) return Compare(left, new xIntX((float)right, DataBitWidth, _unsigned)); if (right is ushort) return Compare(left, new xIntX((ushort)right, DataBitWidth, _unsigned)); if (right is uint) return Compare(left, new xIntX((uint)right, DataBitWidth, _unsigned)); if (right is ulong) return Compare(left, new xIntX((ulong)right, DataBitWidth, _unsigned)); var bytes = right as byte[]; if (bytes != null) return Compare(left, new xIntX(bytes, DataBitWidth, _unsigned)); if (right is Guid) return Compare(left, new xIntX((Guid)right, DataBitWidth, _unsigned)); throw new ArgumentException(); } public static int Compare(xIntX left, xIntX right) { if (ReferenceEquals(left, right)) return 0; if (left.Sign >= 0 && right.Sign < 0) return 1; if (left.Sign < 0 && right.Sign >= 0) return -1; if (left.Data.Length != right.Data.Length) return -1; for (var i = left.Data.Length - 1; i > 0; i--) if (left.Data[i] != right.Data[i]) return left.Data[i].CompareTo(right.Data[i]); return left.Data[0].CompareTo(right.Data[0]); } public static implicit operator xIntX(bool value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(byte value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(char value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(decimal value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(double value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(short value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(int value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(long value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(sbyte value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(float value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(ushort value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(uint value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(ulong value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigInteger value) { if (DataBitWidth == 0) DataBitWidth = value.GetBitWidth(); return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigRational value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigDecimal value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator bool(xIntX value) { return (byte)value.Data[0] != 0; } public static explicit operator byte(xIntX value) { return (byte)value.Data[0]; } public static explicit operator char(xIntX value) { return (char)(ushort)value.Data[0]; } public static explicit operator decimal(xIntX value) { if (value.Sign == 0) return 0; if (value.Data.Length == 1) return new decimal((int)value.Data[0], 0, 0, value.Sign < 0, 0); if (value.Data.Length == 2) return new decimal((int)value.Data[0], (int)value.Data[1], 0, value.Sign < 0, 0); if (value.Data.Length == 3) return new decimal((int)value.Data[0], (int)value.Data[1], (int)value.Data[2], value.Sign < 0, 0); throw new ArgumentException("Value length exceeds decimal length."); } public static explicit operator double(xIntX 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(xIntX 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(xIntX value) { if (value.Data[0] > 0x8000) throw new OverflowException(); if (value.Data[0] == 0x8000 && value.Sign > 0) throw new OverflowException(); return (short)((int)value.Data[0] * value.Sign); } public static explicit operator int(xIntX value) { if (value.Sign == 0) return 0; return (int)value.Data[0] * value.Sign; } public static explicit operator long(xIntX value) { if (value.Sign == 0) return 0; if (value.Data[0] > int.MaxValue) throw new OverflowException(); if (value.Data.Length > 1) if (value.Data[1] != 0) return (long)(((ulong)value.Data[1] << 32) | value.Data[0]) * value.Sign; return value.Data[0] * value.Sign; } public static explicit operator uint(xIntX value) { if (value.Sign == 0) return 0; return value.Data[0]; } public static explicit operator ushort(xIntX value) { if (value.Sign == 0) return 0; return (ushort)value.Data[0]; } public static explicit operator ulong(xIntX value) { if (value.Data.Length > 1) if (value.Data[1] != 0) return ((ulong)value.Data[1] << 32) | value.Data[0]; return value.Data[0]; } public static explicit operator BigInteger(xIntX value) { return new BigInteger(value.ToByteArray()); } public static explicit operator BigDecimal(xIntX value) { return new xIntX(value.ToByteArray()); } public static explicit operator BigRational(xIntX value) { return new BigRational(new BigInteger(value.ToByteArray())); } public static bool operator >(xIntX left, xIntX right) { return left.CompareTo(right) > 0; } public static bool operator <(xIntX left, xIntX right) { return Compare(left, right) < 0; } public static bool operator >=(xIntX left, xIntX right) { return Compare(left, right) >= 0; } public static bool operator <=(xIntX left, xIntX right) { return Compare(left, right) <= 0; } public static bool operator !=(xIntX left, xIntX right) { return !left.Equals(right); } public static bool operator ==(xIntX left, xIntX right) { return left.Equals(right); } public static xIntX operator +(xIntX value) { return value; } public static xIntX operator ~(xIntX value) { var da = new uint[DataLength]; for (var idx = 0; idx < DataLength; idx++) da[idx] = ~value.Data[idx]; return new xIntX(da, DataBitWidth); } public static xIntX operator -(xIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.IsZero) return 0; var da = new uint[DataLength]; for (var i = 0; i < da.Length; i++) da[i] = ~value.Data[i]; var carry = true; var index = 0; while (carry && index < da.Length) { var val = (long)da[index] + 1; da[index] = (uint)(val & AllBits); carry = val >> DataSizeBits > 0; index++; } return new xIntX(da, DataBitWidth); } public static xIntX operator ++(xIntX value) { return value + 1; } public static xIntX operator --(xIntX value) { return value - 1; } public static xIntX Negate(xIntX value) { var ldata = (uint[])value.Data.Clone(); for (var i = 0; i < value.Data.Length; i++) ldata[i] = ~value.Data[i]; return new xIntX(value.Sign, ldata, DataBitWidth, _unsigned); } public static xIntX operator +(xIntX left, xIntX right) { if (right.IsZero) return left; if (left.IsZero) return right; var dl = Math.Max(left.Data.Length, right.Data.Length); var lim = Math.Min(left.Data.Length, right.Data.Length); var result = new uint[dl]; long carry = 0; for (var i = 0; i < dl && i < lim; i++) { var sum = left.Data[i] + (long)right.Data[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 xIntX(left.Sign * right.Sign, result, DataBitWidth, _unsigned); } public static xIntX operator -(xIntX left, xIntX right) { if (right.IsZero) return left; if (left.IsZero) return -right; var size = Math.Max(left.Data.Length, right.Data.Length) + 1; var da = new uint[size]; long carry = 0; for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++) { var diff = left.Data[i] - (long)right.Data[i] - carry; da[i] = (uint)(diff & AllBits); carry = diff < 0 ? 1 : 0; } return new xIntX(da, DataBitWidth); } public static xIntX Add(xIntX left, xIntX right) { return left + right; } public static xIntX Subtract(xIntX left, xIntX right) { return left - right; } public static xIntX Divide(xIntX dividend, xIntX divisor) { if (divisor == 0) throw new DivideByZeroException(); return DivRem(dividend, divisor, out var integer); } public static void Divide(xIntX dividend, xIntX divisor, out xIntX remainder, out xIntX quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quo, out var rem); remainder = new xIntX(1, rem, DataBitWidth, _unsigned); quotient = new xIntX(dividend.Sign * divisor.Sign, quo, DataBitWidth, _unsigned); } public static xIntX DivRem(xIntX dividend, xIntX divisor, out xIntX remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quotient, out var rem); remainder = new xIntX(1, rem, DataBitWidth, _unsigned); return new xIntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth, _unsigned); } 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[1]; 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 xIntX Remainder(xIntX dividend, xIntX divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static xIntX Max(xIntX left, xIntX right) { return left.CompareTo(right) < 0 ? right : left; } public static xIntX Min(xIntX left, xIntX right) { return left.CompareTo(right) <= 0 ? left : right; } public static xIntX operator %(xIntX dividend, xIntX divisor) { return Remainder(dividend, divisor); } public static xIntX operator /(xIntX dividend, xIntX divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { var al = Data.Length >> 1; if (al * 2 != Data.Length) al++; var arr = new ulong[al]; Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2); return arr; } public uint[] ToUIn32Array() { return Data; } public byte[] ToByteArray() { var ba = new byte[Data.Length * DataSize]; Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize); return ba; } private void TrimToMsb() { var dataUsed = Data.Length; while (dataUsed > 1 && Data[dataUsed - 1] == 0) --dataUsed; if (dataUsed != Data.Length) { var tData = new uint[dataUsed]; for (var i = 0; i < dataUsed; i++) tData[i] = Data[i]; Data = (uint[])tData.Clone(); } } public static xIntX Multiply(xIntX left, xIntX right) { if (left == 0 || right == 0) return 0; if (left == 1 && right != 1) return right; if (left != 1 && right == 1) return left; if (left == 1 && right == 1) return 1; var xInts = left.Data; var yInts = right.Data; 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 xIntX(left.Sign * right.Sign, mulInts, DataBitWidth, _unsigned); } public static xIntX operator *(xIntX left, xIntX right) { return Multiply(left, right); } public static xIntX operator >> (xIntX value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value << int.MaxValue << 1; if (shift < 0) return value << -shift; var xd = value.Data; var shiftAmount = 32; var invShift = 0; var bufLen = xd.Length; while (bufLen > 1 && xd[bufLen - 1] == 0) bufLen--; for (var count = shift; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; invShift = 32 - shiftAmount; } ulong carry = 0; for (var i = bufLen - 1; i >= 0; i--) { var val = (ulong)xd[i] >> shiftAmount; val |= carry; carry = (ulong)xd[i] << invShift; xd[i] = (uint)val; } } return new xIntX(value.Sign, xd, DataBitWidth, _unsigned); } public static xIntX operator <<(xIntX 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.Data; 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 xIntX(value.Sign, zd, DataBitWidth, _unsigned); } public static xIntX operator |(xIntX left, xIntX right) { if (left == 0) return right; if (right == 0) return left; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu | yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } public static xIntX operator ^(xIntX left, xIntX right) { var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu ^ yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } public static xIntX operator &(xIntX left, xIntX right) { if (left == 0 || right == 0) return 0; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu & yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } 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 xIntX Pow(xIntX value, xIntX exponent, int bitLength) { 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"); var result = new xIntX("1", bitLength, _unsigned); while (exponent != 0) { if ((exponent & 1) != 0) result *= value; exponent >>= 1; value *= value; } return result; } /// <summary> /// Works well, not as good as BigInteger /// </summary> public static BigInteger ModPow(BigInteger n, BigInteger e, BigInteger m) { var n1 = n; var e1 = e; if (e1 == 0) return 1; if (e1 == 1) return n1 % m; if (e1 == 2) return n1 * n1 % m; n1 %= m; BigInteger r = 1; if ((e1 & 1) == 1) r = n1; while (e1 > 1) { e1 >>= 1; n1 = n1 * n1 % m; if ((e1 & 1) == 1) r = r * n1 % m; } return r; } public static int GetSign(uint[] value) { var allZero = true; for (var i = 0; i < value.Length; i++) if (value[i] != 0) { allZero = false; break; } if (allZero) return 0; return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1; } private static int GetDataUsed(uint[] array) { var neg = GetSign(array) < 0; var dataUsed = array.Length; if (!neg) { while (dataUsed > 1 && array[dataUsed - 1] == 0) --dataUsed; if (dataUsed == 0) dataUsed = 1; } return dataUsed; } public int GetDecimalPlaces(xIntX a) { var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new xIntX(10, DataBitWidth, _unsigned); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } private uint[] TwosComplement(uint[] d) { var i = 0; uint v = 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 { Array.Resize(ref d, d.Length + 1); d[d.Length - 1] = 1; } return d; } public (xIntX approximateRoot, BigRational realRoot) Sqrt() { var n = (BigRational)this; var r = n.Sqrt(); return (r.WholePart, r); } public xIntX Pow(int e) { var ans = this; if (e == 1) return ans; if (e == 0) return 1; for (var i = 1; i != e; i++) ans *= this; return ans; } public static double Log(xIntX value, double baseValue) { var c = 0.0; var d = 0.5; var dataLength = value.DataUsed; var topBits = 0; var x = value.Data[dataLength - 1]; while (x > 0) { x >>= 1; topBits++; } 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.Data[index] & (int)bit) != 0) c += d; d *= 0.5; } bit = 2147483648U; } return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue); } public static List<xIntX> GetFactors(xIntX n) { var Factors = new List<xIntX>(); var s = (xIntX)1 << ((int)Math.Ceiling(Log(n, 2)) >> 1); var a = (xIntX)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 xIntX GreatestCommonDivisor(xIntX a, xIntX b) { while (b > 0) { var r = a % b; a = b; b = r; } return a; } public static xIntX LeastCommonMultiple(xIntX a, xIntX b) { return a * b / a.Gcd(b); } public static double Log10(xIntX value) { return Log(value, 10.0); } public static double LogN(xIntX value) { return Log(value, 2.0); } public void ConstructFromArray(byte[] value, int bitLength) { var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } private class xIntXConverter : 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 xIntX(0, DataBitWidth, _unsigned); } 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); } } } public class xIntXComparer : IComparer<xIntX> { public int Compare(xIntX left, xIntX right) { return left.CompareTo(right); } public bool Equals(xIntX left, xIntX right) { if (left == null || right == null) return false; return left.Equals(right); } public int GetHashCode(xIntX obj) { return obj.GetHashCode(); } }