xIntX.cs

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();
}
}
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(); } }
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();
    }
}

(Obsolete) IntX.cs

Adjustable Bit Width Integer 32,64,128,256,512,1024,2048…

Why not just use BigInteger? Think Hashing which relies on overflow.

Dec-03, 2020: Added ToBinaryString, ToOctalString, Pow, ModPow, GetSign, GetDataUsed, UsedData, BitWidth, GetDecimalPlaces, TwosComplement, Pow, Log, GetFactors, GreatestCommonDivisor, LeastCommonMultiple, Log10, LogN, Sqrt, and ConstructFromArray.

Jun-11,2021: Obsolete Use xIntX Instead.

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.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(IntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable
{
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;
public static readonly IntX One = new IntX(1, 32);
public static readonly IntX Two = new IntX(2, 32);
public static readonly IntX Zero = new IntX(0, 32);
public static readonly IntX Ten = new IntX(10, 32);
public static readonly IntX Three = new IntX(3, 32);
private readonly SerializationInfo SInfo;
public uint[] Data;
public IntX(IntX value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
value.Data.CopyTo(Data, 0);
}
public IntX(string value, int bitLength)
{
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 IntX(byte value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(bool value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = (uint) (value ? 1 : 0);
}
public IntX(char value, int bitLength)
{
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(BigDecimal value, int bitLength)
{
var ba = value.WholePart.ToByteArray();
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var len = ba.Length / DataSize;
Data = new uint[DataLength];
for (var i = 0; i < Data.Length; i++)
Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
}
public IntX(decimal value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-value, DataBitWidth);
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 IntX(double value, int bitLength) : this((decimal) value, bitLength)
{
}
public IntX(float value, int bitLength) : this((decimal) value, bitLength)
{
}
public IntX(short value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(int value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(long value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[1] = (uint) ((value >> 32) & 0xffffffff);
Data[0] = (uint) (value & 0xffffffff);
}
public IntX(sbyte value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(BigRational value, int bitLength)
{
value.Round(1);
ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
}
public IntX(ushort value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(uint value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(ulong value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[1] = (uint) ((value >> 32) & 0xffffffff);
Data[0] = (uint) (value & 0xffffffff);
}
public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public IntX(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 = 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 IntX(int sign, uint[] array, int bitLength)
{
if (array == null)
throw new Exception("Array cannot be null.");
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 (sign < 0)
Data[DataLength - 1] |= HiNeg;
else
Data[DataLength - 1] &= ~HiNeg;
}
public IntX(uint[] array, int bitLength)
{
if (array == null)
throw new Exception("Array cannot be null.");
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);
}
}
protected IntX(SerializationInfo info, StreamingContext context)
{
SInfo = info;
}
private static int DataBitWidth
{
get => _dataBitWidth;
set
{
if (value < 32)
throw new Exception($"Data Bit Width {value} must not be less than 32.");
_dataBitWidth = value;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public IntX MaxValue
{
get
{
var r = new IntX(0, DataBitWidth);
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
{
IntX bw = 1;
var v = new IntX(this, DataBitWidth);
while ((v >>= 1) > 0)
bw++;
if (bw < 8)
bw = 8;
while (bw % 8 != 0)
bw++;
return (int) bw;
}
}
public int Sign
{
get
{
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(IntX 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(IntX 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);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Bits", DataBitWidth);
info.AddValue("Data", Data, typeof(uint[]));
}
public void OnDeserialization(object sender)
{
if (SInfo == null)
return;
DataBitWidth = SInfo.GetInt32("Bits");
if (DataBitWidth != 0)
{
DataLength = DataBitWidth >> 5;
var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
if (array == null)
throw new Exception("Array cannot be null.");
if (array.Length != DataLength)
Array.Resize(ref array, DataLength);
Data = new uint[DataLength];
var ba = new byte[4];
for (var i = 0; i < DataLength; i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
}
}
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()
{
var s = Sign;
var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5;
for (var i = 0; i < Data.Length; i++)
{
hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i];
hash *= 0x1000193;
}
return (int) hash;
}
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 IntX(this, DataBitWidth);
if (negative)
try
{
a = -a;
}
catch (Exception ex)
{
}
var biRadix = new IntX(radix, DataBitWidth);
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 IntX Abs(IntX 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 IntX Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static IntX Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static IntX Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static IntX 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 IntX result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX 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 IntX result)
{
result = new IntX(0, DataBitWidth);
if (digits == null)
return false;
var multiplier = new IntX(1, DataBitWidth);
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 (multiplier.DataUsed == DataLength)
throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
}
if (digits[0] == '-')
result = -result;
return true;
}
public static int Compare(IntX left, object right)
{
if (right is IntX)
return Compare(left, (IntX) right);
if (right is bool)
return Compare(left, new IntX((bool) right, DataBitWidth));
if (right is byte)
return Compare(left, new IntX((byte) right, DataBitWidth));
if (right is char)
return Compare(left, new IntX((char) right, DataBitWidth));
if (right is decimal)
return Compare(left, new IntX((decimal) right, DataBitWidth));
if (right is double)
return Compare(left, new IntX((double) right, DataBitWidth));
if (right is short)
return Compare(left, new IntX((short) right, DataBitWidth));
if (right is int)
return Compare(left, new IntX((int) right, DataBitWidth));
if (right is long)
return Compare(left, new IntX((long) right, DataBitWidth));
if (right is sbyte)
return Compare(left, new IntX((sbyte) right, DataBitWidth));
if (right is float)
return Compare(left, new IntX((float) right, DataBitWidth));
if (right is ushort)
return Compare(left, new IntX((ushort) right, DataBitWidth));
if (right is uint)
return Compare(left, new IntX((uint) right, DataBitWidth));
if (right is ulong)
return Compare(left, new IntX((ulong) right, DataBitWidth));
var bytes = right as byte[];
if (bytes != null)
return Compare(left, new IntX(bytes, DataBitWidth));
if (right is Guid)
return Compare(left, new IntX((Guid) right, DataBitWidth));
throw new ArgumentException();
}
public static int Compare(IntX left, IntX 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 IntX(bool value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(byte value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(char value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(decimal value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(double value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(short value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(int value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(long value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(sbyte value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(float value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(ushort value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(uint value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(ulong value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigInteger value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigRational value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigDecimal value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator bool(IntX value)
{
return (byte) value.Data[0] != 0;
}
public static explicit operator byte(IntX value)
{
return (byte) value.Data[0];
}
public static explicit operator char(IntX value)
{
return (char) (ushort) value.Data[0];
}
public static explicit operator decimal(IntX 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(IntX 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(IntX 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(IntX 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(IntX value)
{
if (value.Sign == 0)
return 0;
return (int) value.Data[0] * value.Sign;
}
public static explicit operator long(IntX 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(IntX value)
{
if (value.Sign == 0)
return 0;
return value.Data[0];
}
public static explicit operator ushort(IntX value)
{
if (value.Sign == 0)
return 0;
return (ushort) value.Data[0];
}
public static explicit operator ulong(IntX 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(IntX value)
{
return new BigInteger(value.ToByteArray());
}
public static explicit operator BigRational(IntX value)
{
return new BigRational(new BigInteger(value.ToByteArray()));
}
public static bool operator >(IntX left, IntX right)
{
return left.CompareTo(right) > 0;
}
public static bool operator <(IntX left, IntX right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(IntX left, IntX right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(IntX left, IntX right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(IntX left, IntX right)
{
return !left.Equals(right);
}
public static bool operator ==(IntX left, IntX right)
{
return left.Equals(right);
}
public static IntX operator +(IntX value)
{
return value;
}
public static IntX operator ~(IntX value)
{
var da = new uint[DataLength];
for (var idx = 0; idx < DataLength; idx++)
da[idx] = ~value.Data[idx];
return new IntX(da, DataBitWidth);
}
public static IntX operator -(IntX value)
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException("value");
if (value.IsZero)
return Zero;
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 IntX(da, DataBitWidth);
}
public static IntX operator ++(IntX value)
{
return value + 1;
}
public static IntX operator --(IntX value)
{
return value - 1;
}
public static IntX Negate(IntX value)
{
var ldata = (uint[]) value.Data.Clone();
for (var i = 0; i < value.Data.Length; i++)
ldata[i] = ~value.Data[i];
return new IntX(value.Sign, ldata, DataBitWidth);
}
public static IntX operator +(IntX left, IntX 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 IntX(left.Sign * right.Sign, result, DataBitWidth);
}
public static IntX operator -(IntX left, IntX 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 IntX(da, DataBitWidth);
}
public static IntX Add(IntX left, IntX right)
{
return left + right;
}
public static IntX Subtract(IntX left, IntX right)
{
return left - right;
}
public static IntX Divide(IntX dividend, IntX divisor)
{
if (divisor == 0)
throw new DivideByZeroException();
return DivRem(dividend, divisor, out var integer);
}
public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
remainder = new IntX(1, rem, DataBitWidth);
quotient = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth);
}
public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
remainder = new IntX(1, rem, DataBitWidth);
return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth);
}
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 IntX Remainder(IntX dividend, IntX divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static IntX Max(IntX left, IntX right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static IntX Min(IntX left, IntX right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static IntX operator %(IntX dividend, IntX divisor)
{
return Remainder(dividend, divisor);
}
public static IntX operator /(IntX dividend, IntX 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 IntX Multiply(IntX left, IntX right)
{
if (left == 0 || right == 0)
return Zero;
if (left == 1 && right != 1)
return right;
if (left != 1 && right == 1)
return left;
if (left == 1 && right == 1)
return One;
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 IntX(left.Sign * right.Sign, mulInts, DataBitWidth);
}
public static IntX operator *(IntX left, IntX right)
{
return Multiply(left, right);
}
public static IntX operator >>(IntX 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 IntX(value.Sign, xd, DataBitWidth);
}
public static IntX operator <<(IntX 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 IntX(value.Sign, zd, DataBitWidth);
}
public static IntX operator |(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
}
public static IntX operator ^(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
}
public static IntX operator &(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
}
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 IntX Pow(IntX value, IntX 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 IntX("1", bitLength);
while (exponent != 0)
{
if ((exponent & 1) != 0)
result *= value;
exponent >>= 1;
value *= value;
}
return result;
}
public static IntX ModPow(IntX n, IntX e, IntX m)
{
var n1 = new IntX(n, DataBitWidth);
var e1 = new IntX(e, DataBitWidth);
var r = new IntX(1, DataBitWidth);
while (e1 != 0)
{
if (e1 % 2 == 1)
r = r * n1 % m;
e1 >>= 1;
n1 = n1 * 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()
{
var dPlaces = 0;
if (Sign == 0)
return 1;
var a = new IntX(this, DataBitWidth);
if (Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new IntX(10, DataBitWidth);
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
a = quotient;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
public (IntX approximateRoot, BigRational realRoot) Sqrt()
{
var n = (BigRational)this;
var r = n.Sqrt();
return (r.GetWholePart, r);
}
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 IntX 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(IntX 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<IntX> GetFactors(IntX n)
{
var Factors = new List<IntX>();
var s = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1);
var a = Three;
while (a < s)
{
if (n % a == 0)
{
Factors.Add(a);
if (a * a != n)
Factors.Add(n / a);
}
a += 2;
}
return Factors;
}
public static IntX GreatestCommonDivisor(IntX a, IntX b)
{
while (b > 0)
{
var r = a % b;
a = b;
b = r;
}
return a;
}
public static IntX LeastCommonMultiple(IntX a, IntX b)
{
return a * b / a.Gcd(b);
}
public static double Log10(IntX value)
{
return Log(value, 10.0);
}
public static double LogN(IntX 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 = 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 IntXConverter : 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 IntX(0, DataBitWidth);
}
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 IntXComparer : IComparer<IntX>
{
public int Compare(IntX left, IntX right)
{
return left.CompareTo(right);
}
public bool Equals(IntX left, IntX right)
{
if (left == null || right == null)
return false;
return left.Equals(right);
}
public int GetHashCode(IntX obj)
{
return obj.GetHashCode();
}
}
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.Runtime.Serialization; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(IntXConverter))] [DebuggerDisplay("{DDisplay}")] public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable { 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; public static readonly IntX One = new IntX(1, 32); public static readonly IntX Two = new IntX(2, 32); public static readonly IntX Zero = new IntX(0, 32); public static readonly IntX Ten = new IntX(10, 32); public static readonly IntX Three = new IntX(3, 32); private readonly SerializationInfo SInfo; public uint[] Data; public IntX(IntX value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; value.Data.CopyTo(Data, 0); } public IntX(string value, int bitLength) { 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 IntX(byte value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(bool value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = (uint) (value ? 1 : 0); } public IntX(char value, int bitLength) { DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(BigDecimal value, int bitLength) { var ba = value.WholePart.ToByteArray(); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var len = ba.Length / DataSize; Data = new uint[DataLength]; for (var i = 0; i < Data.Length; i++) Data[i] = BitConverter.ToUInt32(ba, i * DataSize); } public IntX(decimal value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-value, DataBitWidth); 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 IntX(double value, int bitLength) : this((decimal) value, bitLength) { } public IntX(float value, int bitLength) : this((decimal) value, bitLength) { } public IntX(short value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(int value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(long value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[1] = (uint) ((value >> 32) & 0xffffffff); Data[0] = (uint) (value & 0xffffffff); } public IntX(sbyte value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(BigRational value, int bitLength) { value.Round(1); ConstructFromArray(value.Numerator.ToByteArray(), bitLength); } public IntX(ushort value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(uint value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(ulong value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[1] = (uint) ((value >> 32) & 0xffffffff); Data[0] = (uint) (value & 0xffffffff); } public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength) { } public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength) { } public IntX(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 = 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 IntX(int sign, uint[] array, int bitLength) { if (array == null) throw new Exception("Array cannot be null."); 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 (sign < 0) Data[DataLength - 1] |= HiNeg; else Data[DataLength - 1] &= ~HiNeg; } public IntX(uint[] array, int bitLength) { if (array == null) throw new Exception("Array cannot be null."); 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); } } protected IntX(SerializationInfo info, StreamingContext context) { SInfo = info; } private static int DataBitWidth { get => _dataBitWidth; set { if (value < 32) throw new Exception($"Data Bit Width {value} must not be less than 32."); _dataBitWidth = value; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public IntX MaxValue { get { var r = new IntX(0, DataBitWidth); 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 { IntX bw = 1; var v = new IntX(this, DataBitWidth); while ((v >>= 1) > 0) bw++; if (bw < 8) bw = 8; while (bw % 8 != 0) bw++; return (int) bw; } } public int Sign { get { 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(IntX 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(IntX 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); } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Bits", DataBitWidth); info.AddValue("Data", Data, typeof(uint[])); } public void OnDeserialization(object sender) { if (SInfo == null) return; DataBitWidth = SInfo.GetInt32("Bits"); if (DataBitWidth != 0) { DataLength = DataBitWidth >> 5; var array = (uint[]) SInfo.GetValue("Data", typeof(uint[])); if (array == null) throw new Exception("Array cannot be null."); if (array.Length != DataLength) Array.Resize(ref array, DataLength); Data = new uint[DataLength]; var ba = new byte[4]; for (var i = 0; i < DataLength; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } } } 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() { var s = Sign; var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5; for (var i = 0; i < Data.Length; i++) { hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i]; hash *= 0x1000193; } return (int) hash; } 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 IntX(this, DataBitWidth); if (negative) try { a = -a; } catch (Exception ex) { } var biRadix = new IntX(radix, DataBitWidth); 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 IntX Abs(IntX 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 IntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static IntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static IntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static IntX 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 IntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX 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 IntX result) { result = new IntX(0, DataBitWidth); if (digits == null) return false; var multiplier = new IntX(1, DataBitWidth); 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 (multiplier.DataUsed == DataLength) throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} "); } if (digits[0] == '-') result = -result; return true; } public static int Compare(IntX left, object right) { if (right is IntX) return Compare(left, (IntX) right); if (right is bool) return Compare(left, new IntX((bool) right, DataBitWidth)); if (right is byte) return Compare(left, new IntX((byte) right, DataBitWidth)); if (right is char) return Compare(left, new IntX((char) right, DataBitWidth)); if (right is decimal) return Compare(left, new IntX((decimal) right, DataBitWidth)); if (right is double) return Compare(left, new IntX((double) right, DataBitWidth)); if (right is short) return Compare(left, new IntX((short) right, DataBitWidth)); if (right is int) return Compare(left, new IntX((int) right, DataBitWidth)); if (right is long) return Compare(left, new IntX((long) right, DataBitWidth)); if (right is sbyte) return Compare(left, new IntX((sbyte) right, DataBitWidth)); if (right is float) return Compare(left, new IntX((float) right, DataBitWidth)); if (right is ushort) return Compare(left, new IntX((ushort) right, DataBitWidth)); if (right is uint) return Compare(left, new IntX((uint) right, DataBitWidth)); if (right is ulong) return Compare(left, new IntX((ulong) right, DataBitWidth)); var bytes = right as byte[]; if (bytes != null) return Compare(left, new IntX(bytes, DataBitWidth)); if (right is Guid) return Compare(left, new IntX((Guid) right, DataBitWidth)); throw new ArgumentException(); } public static int Compare(IntX left, IntX 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 IntX(bool value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(byte value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(char value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(decimal value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(double value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(short value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(int value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(long value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(sbyte value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(float value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(ushort value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(uint value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(ulong value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigInteger value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigRational value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigDecimal value) { return new IntX(value, DataBitWidth); } public static explicit operator bool(IntX value) { return (byte) value.Data[0] != 0; } public static explicit operator byte(IntX value) { return (byte) value.Data[0]; } public static explicit operator char(IntX value) { return (char) (ushort) value.Data[0]; } public static explicit operator decimal(IntX 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(IntX 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(IntX 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(IntX 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(IntX value) { if (value.Sign == 0) return 0; return (int) value.Data[0] * value.Sign; } public static explicit operator long(IntX 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(IntX value) { if (value.Sign == 0) return 0; return value.Data[0]; } public static explicit operator ushort(IntX value) { if (value.Sign == 0) return 0; return (ushort) value.Data[0]; } public static explicit operator ulong(IntX 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(IntX value) { return new BigInteger(value.ToByteArray()); } public static explicit operator BigRational(IntX value) { return new BigRational(new BigInteger(value.ToByteArray())); } public static bool operator >(IntX left, IntX right) { return left.CompareTo(right) > 0; } public static bool operator <(IntX left, IntX right) { return Compare(left, right) < 0; } public static bool operator >=(IntX left, IntX right) { return Compare(left, right) >= 0; } public static bool operator <=(IntX left, IntX right) { return Compare(left, right) <= 0; } public static bool operator !=(IntX left, IntX right) { return !left.Equals(right); } public static bool operator ==(IntX left, IntX right) { return left.Equals(right); } public static IntX operator +(IntX value) { return value; } public static IntX operator ~(IntX value) { var da = new uint[DataLength]; for (var idx = 0; idx < DataLength; idx++) da[idx] = ~value.Data[idx]; return new IntX(da, DataBitWidth); } public static IntX operator -(IntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.IsZero) return Zero; 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 IntX(da, DataBitWidth); } public static IntX operator ++(IntX value) { return value + 1; } public static IntX operator --(IntX value) { return value - 1; } public static IntX Negate(IntX value) { var ldata = (uint[]) value.Data.Clone(); for (var i = 0; i < value.Data.Length; i++) ldata[i] = ~value.Data[i]; return new IntX(value.Sign, ldata, DataBitWidth); } public static IntX operator +(IntX left, IntX 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 IntX(left.Sign * right.Sign, result, DataBitWidth); } public static IntX operator -(IntX left, IntX 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 IntX(da, DataBitWidth); } public static IntX Add(IntX left, IntX right) { return left + right; } public static IntX Subtract(IntX left, IntX right) { return left - right; } public static IntX Divide(IntX dividend, IntX divisor) { if (divisor == 0) throw new DivideByZeroException(); return DivRem(dividend, divisor, out var integer); } public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quo, out var rem); remainder = new IntX(1, rem, DataBitWidth); quotient = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth); } public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quotient, out var rem); remainder = new IntX(1, rem, DataBitWidth); return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth); } 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 IntX Remainder(IntX dividend, IntX divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static IntX Max(IntX left, IntX right) { return left.CompareTo(right) < 0 ? right : left; } public static IntX Min(IntX left, IntX right) { return left.CompareTo(right) <= 0 ? left : right; } public static IntX operator %(IntX dividend, IntX divisor) { return Remainder(dividend, divisor); } public static IntX operator /(IntX dividend, IntX 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 IntX Multiply(IntX left, IntX right) { if (left == 0 || right == 0) return Zero; if (left == 1 && right != 1) return right; if (left != 1 && right == 1) return left; if (left == 1 && right == 1) return One; 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 IntX(left.Sign * right.Sign, mulInts, DataBitWidth); } public static IntX operator *(IntX left, IntX right) { return Multiply(left, right); } public static IntX operator >>(IntX 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 IntX(value.Sign, xd, DataBitWidth); } public static IntX operator <<(IntX 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 IntX(value.Sign, zd, DataBitWidth); } public static IntX operator |(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth); } public static IntX operator ^(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth); } public static IntX operator &(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth); } 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 IntX Pow(IntX value, IntX 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 IntX("1", bitLength); while (exponent != 0) { if ((exponent & 1) != 0) result *= value; exponent >>= 1; value *= value; } return result; } public static IntX ModPow(IntX n, IntX e, IntX m) { var n1 = new IntX(n, DataBitWidth); var e1 = new IntX(e, DataBitWidth); var r = new IntX(1, DataBitWidth); while (e1 != 0) { if (e1 % 2 == 1) r = r * n1 % m; e1 >>= 1; n1 = n1 * 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() { var dPlaces = 0; if (Sign == 0) return 1; var a = new IntX(this, DataBitWidth); if (Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new IntX(10, DataBitWidth); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } public (IntX approximateRoot, BigRational realRoot) Sqrt() { var n = (BigRational)this; var r = n.Sqrt(); return (r.GetWholePart, r); } 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 IntX 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(IntX 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<IntX> GetFactors(IntX n) { var Factors = new List<IntX>(); var s = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1); var a = Three; while (a < s) { if (n % a == 0) { Factors.Add(a); if (a * a != n) Factors.Add(n / a); } a += 2; } return Factors; } public static IntX GreatestCommonDivisor(IntX a, IntX b) { while (b > 0) { var r = a % b; a = b; b = r; } return a; } public static IntX LeastCommonMultiple(IntX a, IntX b) { return a * b / a.Gcd(b); } public static double Log10(IntX value) { return Log(value, 10.0); } public static double LogN(IntX 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 = 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 IntXConverter : 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 IntX(0, DataBitWidth); } 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 IntXComparer : IComparer<IntX> { public int Compare(IntX left, IntX right) { return left.CompareTo(right); } public bool Equals(IntX left, IntX right) { if (left == null || right == null) return false; return left.Equals(right); } public int GetHashCode(IntX obj) { return obj.GetHashCode(); } }
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.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(IntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable
{
    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;
    public static readonly IntX              One   = new IntX(1,  32);
    public static readonly IntX              Two   = new IntX(2,  32);
    public static readonly IntX              Zero  = new IntX(0,  32);
    public static readonly IntX              Ten   = new IntX(10, 32);
    public static readonly IntX              Three = new IntX(3,  32);
    private readonly       SerializationInfo SInfo;
    public                 uint[]            Data;
    public IntX(IntX value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        value.Data.CopyTo(Data, 0);
    }
    public IntX(string value, int bitLength)
    {
        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 IntX(byte value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(bool value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = (uint) (value ? 1 : 0);
    }
    public IntX(char value, int bitLength)
    {
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public IntX(BigDecimal value, int bitLength)
    {
        var ba = value.WholePart.ToByteArray();
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        var len = ba.Length / DataSize;
        Data = new uint[DataLength];
        for (var i = 0; i < Data.Length; i++)
            Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
    }
    public IntX(decimal value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-value, DataBitWidth);
            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 IntX(double value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public IntX(float value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public IntX(short value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(int value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(long value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[1] = (uint) ((value >> 32) & 0xffffffff);
        Data[0] = (uint) (value         & 0xffffffff);
    }
    public IntX(sbyte value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(BigRational value, int bitLength)
    {
        value.Round(1);
        ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
    }
    public IntX(ushort value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(uint value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(ulong value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[1]      = (uint) ((value >> 32) & 0xffffffff);
        Data[0]      = (uint) (value         & 0xffffffff);
    }
    public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public IntX(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     = 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 IntX(int sign, uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        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 (sign < 0)
            Data[DataLength - 1] |= HiNeg;
        else
            Data[DataLength - 1] &= ~HiNeg;
    }
    public IntX(uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        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);
        }
    }
    protected IntX(SerializationInfo info, StreamingContext context)
    {
        SInfo = info;
    }
    private static int DataBitWidth
    {
        get => _dataBitWidth;
        set
        {
            if (value < 32)
                throw new Exception($"Data Bit Width {value} must not be less than 32.");
            _dataBitWidth = value;
        }
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => ToString();
    public IntX MaxValue
    {
        get
        {
            var r = new IntX(0, DataBitWidth);
            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
        {
            IntX bw = 1;
            var  v  = new IntX(this, DataBitWidth);
            while ((v >>= 1) > 0)
                bw++;
            if (bw < 8)
                bw = 8;
            while (bw % 8 != 0)
                bw++;
            return (int) bw;
        }
    }
    public int Sign
    {
        get
        {
            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(IntX 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(IntX 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);
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bits", DataBitWidth);
        info.AddValue("Data", Data, typeof(uint[]));
    }
    public void OnDeserialization(object sender)
    {
        if (SInfo == null)
            return;
        DataBitWidth = SInfo.GetInt32("Bits");
        if (DataBitWidth != 0)
        {
            DataLength = DataBitWidth >> 5;
            var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
            if (array == null)
                throw new Exception("Array cannot be null.");
            if (array.Length != DataLength)
                Array.Resize(ref array, DataLength);
            Data = new uint[DataLength];
            var ba = new byte[4];
            for (var i = 0; i < DataLength; i++)
            {
                Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
                Data[i] = BitConverter.ToUInt32(ba, 0);
            }
        }
    }
    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()
    {
        var s    = Sign;
        var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5;
        for (var i = 0; i < Data.Length; i++)
        {
            hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i];
            hash *= 0x1000193;
        }
        return (int) hash;
    }
    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 IntX(this, DataBitWidth);
        if (negative)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
            }
        var          biRadix = new IntX(radix, DataBitWidth);
        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 IntX Abs(IntX 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 IntX Parse(string value)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
    }
    public static IntX Parse(string value, NumberStyles style)
    {
        return Parse(value, style, NumberFormatInfo.CurrentInfo);
    }
    public static IntX Parse(string value, IFormatProvider provider)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
    }
    public static IntX 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 IntX result)
    {
        return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
    }
    public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX 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 IntX result)
    {
        result = new IntX(0, DataBitWidth);
        if (digits == null)
            return false;
        var multiplier = new IntX(1, DataBitWidth);
        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 (multiplier.DataUsed == DataLength)
                throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
        }
        if (digits[0] == '-')
            result = -result;
        return true;
    }
    public static int Compare(IntX left, object right)
    {
        if (right is IntX)
            return Compare(left, (IntX) right);
        if (right is bool)
            return Compare(left, new IntX((bool) right, DataBitWidth));
        if (right is byte)
            return Compare(left, new IntX((byte) right, DataBitWidth));
        if (right is char)
            return Compare(left, new IntX((char) right, DataBitWidth));
        if (right is decimal)
            return Compare(left, new IntX((decimal) right, DataBitWidth));
        if (right is double)
            return Compare(left, new IntX((double) right, DataBitWidth));
        if (right is short)
            return Compare(left, new IntX((short) right, DataBitWidth));
        if (right is int)
            return Compare(left, new IntX((int) right, DataBitWidth));
        if (right is long)
            return Compare(left, new IntX((long) right, DataBitWidth));
        if (right is sbyte)
            return Compare(left, new IntX((sbyte) right, DataBitWidth));
        if (right is float)
            return Compare(left, new IntX((float) right, DataBitWidth));
        if (right is ushort)
            return Compare(left, new IntX((ushort) right, DataBitWidth));
        if (right is uint)
            return Compare(left, new IntX((uint) right, DataBitWidth));
        if (right is ulong)
            return Compare(left, new IntX((ulong) right, DataBitWidth));
        var bytes = right as byte[];
        if (bytes != null)
            return Compare(left, new IntX(bytes, DataBitWidth));
        if (right is Guid)
            return Compare(left, new IntX((Guid) right, DataBitWidth));
        throw new ArgumentException();
    }
    public static int Compare(IntX left, IntX 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 IntX(bool value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(byte value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(char value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(decimal value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(double value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(short value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(int value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(long value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(sbyte value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(float value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(ushort value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(uint value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(ulong value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigInteger value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigRational value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigDecimal value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator bool(IntX value)
    {
        return (byte) value.Data[0] != 0;
    }
    public static explicit operator byte(IntX value)
    {
        return (byte) value.Data[0];
    }
    public static explicit operator char(IntX value)
    {
        return (char) (ushort) value.Data[0];
    }
    public static explicit operator decimal(IntX 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(IntX 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(IntX 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(IntX 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(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (int) value.Data[0] * value.Sign;
    }
    public static explicit operator long(IntX 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(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return value.Data[0];
    }
    public static explicit operator ushort(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (ushort) value.Data[0];
    }
    public static explicit operator ulong(IntX 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(IntX value)
    {
        return new BigInteger(value.ToByteArray());
    }
    public static explicit operator BigRational(IntX value)
    {
        return new BigRational(new BigInteger(value.ToByteArray()));
    }
    public static bool operator >(IntX left, IntX right)
    {
        return left.CompareTo(right) > 0;
    }
    public static bool operator <(IntX left, IntX right)
    {
        return Compare(left, right) < 0;
    }
    public static bool operator >=(IntX left, IntX right)
    {
        return Compare(left, right) >= 0;
    }
    public static bool operator <=(IntX left, IntX right)
    {
        return Compare(left, right) <= 0;
    }
    public static bool operator !=(IntX left, IntX right)
    {
        return !left.Equals(right);
    }
    public static bool operator ==(IntX left, IntX right)
    {
        return left.Equals(right);
    }
    public static IntX operator +(IntX value)
    {
        return value;
    }
    public static IntX operator ~(IntX value)
    {
        var da = new uint[DataLength];
        for (var idx = 0; idx < DataLength; idx++)
            da[idx] = ~value.Data[idx];
        return new IntX(da, DataBitWidth);
    }
    public static IntX operator -(IntX value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.IsZero)
            return Zero;
        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 IntX(da, DataBitWidth);
    }
    public static IntX operator ++(IntX value)
    {
        return value + 1;
    }
    public static IntX operator --(IntX value)
    {
        return value - 1;
    }
    public static IntX Negate(IntX value)
    {
        var ldata = (uint[]) value.Data.Clone();
        for (var i = 0; i < value.Data.Length; i++)
            ldata[i] = ~value.Data[i];
        return new IntX(value.Sign, ldata, DataBitWidth);
    }
    public static IntX operator +(IntX left, IntX 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 IntX(left.Sign * right.Sign, result, DataBitWidth);
    }
    public static IntX operator -(IntX left, IntX 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 IntX(da, DataBitWidth);
    }
    public static IntX Add(IntX left, IntX right)
    {
        return left + right;
    }
    public static IntX Subtract(IntX left, IntX right)
    {
        return left - right;
    }
    public static IntX Divide(IntX dividend, IntX divisor)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        return DivRem(dividend, divisor, out var integer);
    }
    public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
        remainder = new IntX(1,                            rem, DataBitWidth);
        quotient  = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth);
    }
    public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
        remainder = new IntX(1, rem, DataBitWidth);
        return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth);
    }
    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 IntX Remainder(IntX dividend, IntX divisor)
    {
        DivRem(dividend, divisor, out var remainder);
        return remainder;
    }
    public static IntX Max(IntX left, IntX right)
    {
        return left.CompareTo(right) < 0 ? right : left;
    }
    public static IntX Min(IntX left, IntX right)
    {
        return left.CompareTo(right) <= 0 ? left : right;
    }
    public static IntX operator %(IntX dividend, IntX divisor)
    {
        return Remainder(dividend, divisor);
    }
    public static IntX operator /(IntX dividend, IntX 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 IntX Multiply(IntX left, IntX right)
    {
        if (left == 0 || right == 0)
            return Zero;
        if (left == 1 && right != 1)
            return right;
        if (left != 1 && right == 1)
            return left;
        if (left == 1 && right == 1)
            return One;
        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 IntX(left.Sign * right.Sign, mulInts, DataBitWidth);
    }
    public static IntX operator *(IntX left, IntX right)
    {
        return Multiply(left, right);
    }
    public static IntX operator >>(IntX 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 IntX(value.Sign, xd, DataBitWidth);
    }
    public static IntX operator <<(IntX 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 IntX(value.Sign, zd, DataBitWidth);
    }
    public static IntX operator |(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static IntX operator ^(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static IntX operator &(IntX left, IntX 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 IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    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 IntX Pow(IntX value, IntX 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 IntX("1", bitLength);
        while (exponent != 0)
        {
            if ((exponent & 1) != 0)
                result *= value;
            exponent >>= 1;
            value    *=  value;
        }
        return result;
    }
    public static IntX ModPow(IntX n, IntX e, IntX m)
    {
        var n1 = new IntX(n, DataBitWidth);
        var e1 = new IntX(e, DataBitWidth);
        var r  = new IntX(1, DataBitWidth);
        while (e1 != 0)
        {
            if (e1 % 2 == 1)
                r = r * n1 % m;
            e1 >>= 1;
            n1 =   n1 * 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()
    {
        var dPlaces = 0;
        if (Sign == 0)
            return 1;
        var a = new IntX(this, DataBitWidth);
        if (Sign < 0)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
                return 0;
            }
        var biRadix = new IntX(10, DataBitWidth);
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                a = quotient;
                dPlaces++;
            }
            catch (Exception ex)
            {
                break;
            }
        return dPlaces;
    }
    public (IntX approximateRoot, BigRational realRoot) Sqrt()
    {
        var n = (BigRational)this;
        var r = n.Sqrt();
        return (r.GetWholePart, r);
    }
    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 IntX 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(IntX 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<IntX> GetFactors(IntX n)
    {
        var Factors = new List<IntX>();
        var s       = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1);
        var a       = Three;
        while (a < s)
        {
            if (n % a == 0)
            {
                Factors.Add(a);
                if (a * a != n)
                    Factors.Add(n / a);
            }
            a += 2;
        }
        return Factors;
    }
    public static IntX GreatestCommonDivisor(IntX a, IntX b)
    {
        while (b > 0)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static IntX LeastCommonMultiple(IntX a, IntX b)
    {
        return a * b / a.Gcd(b);
    }
    public static double Log10(IntX value)
    {
        return Log(value, 10.0);
    }
    public static double LogN(IntX 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 = 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 IntXConverter : 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 IntX(0, DataBitWidth);
        }
        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 IntXComparer : IComparer<IntX>
{
    public int Compare(IntX left, IntX right)
    {
        return left.CompareTo(right);
    }
    public bool Equals(IntX left, IntX right)
    {
        if (left == null || right == null)
            return false;
        return left.Equals(right);
    }
    public int GetHashCode(IntX obj)
    {
        return obj.GetHashCode();
    }
}

(Obsolete) UIntX.cs

Adjustable Bit Width Unsigned Integer 32,64,128,256,512,1024,2048…

Why not just use BigInteger? Think Hashing which relies on overflow.

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.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(UIntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class UIntX : IComparable<UIntX>, IComparable, IEquatable<UIntX>, IConvertible, IFormattable, ISerializable
{
private const int DataSize = sizeof(uint);
private static int DataBitLength;
private static int DataLength;
public static UIntX Zero = new UIntX("0", 32);
public static UIntX Ten = new UIntX("10", 32);
public static UIntX One = new UIntX("1", 32);
private readonly SerializationInfo SInfo;
private uint[] data;
public UIntX(UIntX value, int bitLength)
{
if (value == null)
return;
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
value.data.CopyTo(data, 0);
}
public UIntX(string value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
if (!TryParse(value, out var result))
throw new Exception("TryParse Failed.");
data = new uint[DataLength];
result.data.CopyTo(data, 0);
}
public UIntX(byte value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = value;
}
public UIntX(bool value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = (uint) (value ? 1 : 0);
}
public UIntX(char value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = value;
}
public UIntX(BigDecimal value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
var ba = value.UnscaledValue.ToByteArray();
data = new uint[DataLength];
for (var i = 0; i < DataLength; i++)
data[i] = BitConverter.ToUInt32(ba, i * DataSize);
}
public UIntX(decimal value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
if (value < 0)
{
var n = -new UIntX(-value, DataBitLength);
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 UIntX(double value, int bitLength) : this((decimal) value, bitLength)
{
}
public UIntX(float value, int bitLength) : this((decimal) value, bitLength)
{
}
public UIntX(short value, int bitLength) : this((int) value, bitLength)
{
}
public UIntX(int value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = (uint) value;
}
public UIntX(long value, int bitLength) : this((ulong) value, bitLength)
{
}
public UIntX(sbyte value, int bitLength) : this((int) value, bitLength)
{
}
public UIntX(ushort value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = value;
}
public UIntX(uint value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = value;
}
public UIntX(ulong value, int bitLength)
{
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
data[0] = (uint) ((value >> 32) & 0xffffffff);
data[1] = (uint) (value & 0xffffffff);
}
public UIntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public UIntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public UIntX(byte[] value, int bitLength)
{
if (value == null)
throw new ArgumentNullException("value");
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
var minSize = value.Length / DataSize;
var byteCount = value.Length;
var isNegative = 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, minSize)];
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 UIntX(uint[] array, int bitLength)
{
if (array == null)
throw new Exception("Array cannot be null.");
DataBitLength = bitLength;
DataLength = DataBitLength >> 5;
data = new uint[DataLength];
var ba = new byte[4];
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);
}
}
protected UIntX(SerializationInfo info, StreamingContext context)
{
SInfo = info;
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public UIntX MaxValue => (One << DataBitLength) - 1;
public bool IsOne => this == One;
public bool IsEven => this % 2 == 0;
public bool IsZero
{
get
{
for (var i = 0; i < data.Length; i++)
if (data[i] != 0)
return false;
return true;
}
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public int CompareTo(UIntX 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 (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 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)
throw new OverflowException();
return data[0];
}
public bool Equals(UIntX obj)
{
if (ReferenceEquals(obj, null))
return false;
if (ReferenceEquals(this, obj))
return true;
if (data.Length != obj.data.Length)
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', 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);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Bits", DataBitLength);
info.AddValue("Data", data, typeof(uint[]));
}
public void OnDeserialization(object sender)
{
if (SInfo == null)
return;
DataBitLength = SInfo.GetInt32("Bits");
if (DataBitLength != 0)
{
DataLength = DataBitLength >> 5;
var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
if (array == null)
throw new Exception("Array cannot be null.");
if (array.Length != DataLength)
Array.Resize(ref array, DataLength);
data = new uint[DataLength];
var ba = new byte[4];
for (var i = 0; i < DataLength; i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
data[i] = BitConverter.ToUInt32(ba, 0);
}
}
}
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()
{
var hash = 0x811c9dc5;
for (var i = 0; i < DataLength; i++)
{
hash ^= ((hash << 13) | (hash >> 19)) ^ data[i];
hash *= 0x1000193;
}
return (int) hash;
}
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);
}
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 (IsZero)
return "0";
var a = new UIntX(this, DataBitLength);
var biRadix = new UIntX(radix, DataBitLength);
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)));
return result;
}
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 UIntX Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static UIntX Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static UIntX Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static UIntX 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 UIntX result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out UIntX 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 UIntX result)
{
result = new UIntX(0, DataBitLength);
if (digits == null)
return false;
var multiplier = new UIntX(1, DataBitLength);
digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
var nDigits = digits[0] == '-' ? 1 : 0;
for (var idx = digits.Length - 1; idx >= nDigits; idx--)
{
var d = (int) digits[idx];
if (d >= '0' && d <= '9')
d -= '0';
else if (d >= 'A' && d <= 'Z')
d = d - 'A' + 10;
else
return false;
if (d >= radix)
return false;
result += multiplier * d;
multiplier *= radix;
}
if (digits[0] == '-')
result = -result;
return true;
}
public static int Compare(UIntX left, object right)
{
if (right is UIntX)
return Compare(left, (UIntX) right);
if (right is bool)
return Compare(left, new UIntX((bool) right, DataBitLength));
if (right is byte)
return Compare(left, new UIntX((byte) right, DataBitLength));
if (right is char)
return Compare(left, new UIntX((char) right, DataBitLength));
if (right is decimal)
return Compare(left, new UIntX((decimal) right, DataBitLength));
if (right is double)
return Compare(left, new UIntX((double) right, DataBitLength));
if (right is short)
return Compare(left, new UIntX((short) right, DataBitLength));
if (right is int)
return Compare(left, new UIntX((int) right, DataBitLength));
if (right is long)
return Compare(left, new UIntX((long) right, DataBitLength));
if (right is sbyte)
return Compare(left, new UIntX((sbyte) right, DataBitLength));
if (right is float)
return Compare(left, new UIntX((float) right, DataBitLength));
if (right is ushort)
return Compare(left, new UIntX((ushort) right, DataBitLength));
if (right is uint)
return Compare(left, new UIntX((uint) right, DataBitLength));
if (right is ulong)
return Compare(left, new UIntX((ulong) right, DataBitLength));
var bytes = right as byte[];
if (bytes != null && bytes.Length != 64)
return Compare(left, new UIntX(bytes, DataBitLength));
if (right is Guid)
return Compare(left, new UIntX((Guid) right, DataBitLength));
throw new ArgumentException();
}
public static int Compare(UIntX left, UIntX right)
{
if (left.data.Length < right.data.Length)
return -1;
if (ReferenceEquals(left, right))
return 0;
if (ReferenceEquals(left, null))
throw new ArgumentNullException("left");
if (ReferenceEquals(right, null))
throw new ArgumentNullException("right");
if (left > right) return 1;
if (left == right) return 0;
return -1;
}
public static implicit operator UIntX(bool value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(byte value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(char value)
{
return new UIntX(value, DataBitLength);
}
public static explicit operator UIntX(decimal value)
{
return new UIntX(value, DataBitLength);
}
public static explicit operator UIntX(double value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(short value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(int value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(long value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(sbyte value)
{
return new UIntX(value, DataBitLength);
}
public static explicit operator UIntX(float value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(ushort value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(uint value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(ulong value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(BigInteger value)
{
return new UIntX(value, DataBitLength);
}
public static implicit operator UIntX(BigDecimal value)
{
return new UIntX(value, DataBitLength);
}
public static explicit operator bool(UIntX value)
{
return (byte) value.data[0] != 0;
}
public static explicit operator byte(UIntX value)
{
return (byte) value.data[0];
}
public static explicit operator char(UIntX value)
{
return (char) (ushort) value.data[0];
}
public static explicit operator decimal(UIntX value)
{
return new decimal((int) value.data[0], (int) value.data[1], (int) value.data[2], false, 0);
}
public static explicit operator double(UIntX value)
{
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(UIntX value)
{
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(UIntX value)
{
return (short) (int) value.data[0];
}
public static explicit operator int(UIntX value)
{
return (int) value.data[0];
}
public static explicit operator long(UIntX value)
{
if (value.data[1] != 0)
return (long) (((ulong) value.data[1] << 32) | value.data[0]);
return value.data[0];
}
public static explicit operator uint(UIntX value)
{
return value.data[0];
}
public static explicit operator ushort(UIntX value)
{
return (ushort) value.data[0];
}
public static explicit operator ulong(UIntX value)
{
if (value.data[1] != 0)
return ((ulong) value.data[1] << 32) | value.data[0];
return value.data[0];
}
public static explicit operator BigInteger(UIntX value)
{
return new BigInteger(value.ToByteArray());
}
public static bool operator >(UIntX left, UIntX right)
{
if (left.data.Length > right.data.Length)
return false;
if (left.data.Length < right.data.Length)
return true;
if (ReferenceEquals(left, null))
throw new ArgumentNullException("left");
if (ReferenceEquals(right, null))
throw new ArgumentNullException("right");
for (var i = 0; i < DataLength; i++)
if (left.data[i] != right.data[i])
return left.data[i] > right.data[i];
return false;
}
public static bool operator <(UIntX left, UIntX right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(UIntX left, UIntX right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(UIntX left, UIntX right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(UIntX left, UIntX right)
{
return Compare(left, right) != 0;
}
public static bool operator ==(UIntX left, UIntX right)
{
if (ReferenceEquals(left, right))
return true;
if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
return false;
return left.Equals(right);
}
public static UIntX operator +(UIntX value)
{
return value;
}
public static UIntX operator ~(UIntX value)
{
var da = new uint[DataLength];
for (var idx = 0; idx < DataLength; idx++)
da[idx] = ~value.data[idx];
return new UIntX(da, DataBitLength);
}
public static UIntX operator -(UIntX value)
{
return Negate(value);
}
public static UIntX operator ++(UIntX value)
{
return value + 1;
}
public static UIntX operator --(UIntX value)
{
return value - 1;
}
public static UIntX Negate(UIntX value)
{
for (var i = 0; i < DataLength; i++)
value.data[i] = ~value.data[i];
return new UIntX(value, DataBitLength);
}
public static UIntX operator +(UIntX left, UIntX right)
{
var dl = left.data.Length > right.data.Length ? left.data.Length : right.data.Length;
var result = new uint[dl];
long carry = 0;
for (var i = 0; i < dl; 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 UIntX(result, DataBitLength);
}
public static UIntX operator -(UIntX left, UIntX 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 < da.Length - 1; i++)
{
var diff = left.data[i] - (long) right.data[i] - carry;
da[i] = (uint) (diff & DigitsArray.AllBits);
carry = diff < 0 ? 1 : 0;
}
return new UIntX(da, DataBitLength);
}
public static UIntX Add(UIntX left, UIntX right)
{
return left + right;
}
public static UIntX Subtract(UIntX left, UIntX right)
{
return left - right;
}
public static UIntX Divide(UIntX dividend, UIntX divisor)
{
return DivRem(dividend, divisor, out var integer);
}
public static void Divide(UIntX dividend, UIntX divisor, out UIntX remainder, out UIntX quotient)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.data, divisor.data, out var quo, out var rem);
remainder = new UIntX(rem, DataBitLength);
quotient = new UIntX(quo, DataBitLength);
}
public static UIntX DivRem(UIntX dividend, UIntX divisor, out UIntX remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.data, divisor.data, out var quotient, out var rem);
remainder = new UIntX(rem, DataBitLength);
return new UIntX(quotient, DataBitLength);
}
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 UIntX Remainder(UIntX dividend, UIntX divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static UIntX Max(UIntX left, UIntX right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static UIntX Min(UIntX left, UIntX right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static UIntX operator %(UIntX dividend, UIntX divisor)
{
return Remainder(dividend, divisor);
}
public static UIntX operator /(UIntX dividend, UIntX 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;
}
public byte[] ToByteArray(int length)
{
if (length <= 0 || length > data.Length * DataSize)
throw new ArgumentException($"Length {length} out of range length > 0 or length <= {data.Length * DataSize}");
var ba = new byte[length];
Buffer.BlockCopy(data, 0, ba, 0, length);
return ba;
}
public static UIntX Multiply(UIntX left, UIntX right)
{
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 UIntX(mulInts, DataBitLength);
}
public static UIntX operator *(UIntX left, UIntX right)
{
return Multiply(left, right);
}
public static UIntX operator >>(UIntX value, int shift)
{
if (value == Zero)
return Zero;
var xd = (uint[]) value.data.Clone();
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 UIntX(xd, DataBitLength);
}
public static UIntX operator <<(UIntX value, int shift)
{
if (value == Zero)
return Zero;
var digitShift = shift / 32;
var smallShift = shift - digitShift * 32;
var xd = (uint[]) value.data.Clone();
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 UIntX(zd, DataBitLength);
}
public static UIntX operator |(UIntX left, UIntX right)
{
if (left == 0)
return right;
if (right == 0)
return left;
var z = new uint[Math.Max(left.data.Length, right.data.Length)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.data.Length ? left.data[i] : 0U;
var yu = i < right.data.Length ? right.data[i] : 0U;
z[i] = xu | yu;
}
return new UIntX(z, DataBitLength);
}
public static UIntX operator ^(UIntX left, UIntX right)
{
var z = new uint[Math.Max(left.data.Length, right.data.Length)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.data.Length ? left.data[i] : 0U;
var yu = i < right.data.Length ? right.data[i] : 0U;
z[i] = xu ^ yu;
}
return new UIntX(z, DataBitLength);
}
public static UIntX operator &(UIntX left, UIntX right)
{
if (left == 0 || right == 0)
return Zero;
var z = new uint[Math.Max(left.data.Length, right.data.Length)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.data.Length ? left.data[i] : 0U;
var yu = i < right.data.Length ? right.data[i] : 0U;
z[i] = xu & yu;
}
return new UIntX(z, DataBitLength);
}
private class UIntXConverter : 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 UIntX("0", DataBitLength);
}
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);
}
}
}
using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(UIntXConverter))] [DebuggerDisplay("{DDisplay}")] public class UIntX : IComparable<UIntX>, IComparable, IEquatable<UIntX>, IConvertible, IFormattable, ISerializable { private const int DataSize = sizeof(uint); private static int DataBitLength; private static int DataLength; public static UIntX Zero = new UIntX("0", 32); public static UIntX Ten = new UIntX("10", 32); public static UIntX One = new UIntX("1", 32); private readonly SerializationInfo SInfo; private uint[] data; public UIntX(UIntX value, int bitLength) { if (value == null) return; DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; value.data.CopyTo(data, 0); } public UIntX(string value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); data = new uint[DataLength]; result.data.CopyTo(data, 0); } public UIntX(byte value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = value; } public UIntX(bool value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = (uint) (value ? 1 : 0); } public UIntX(char value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = value; } public UIntX(BigDecimal value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; var ba = value.UnscaledValue.ToByteArray(); data = new uint[DataLength]; for (var i = 0; i < DataLength; i++) data[i] = BitConverter.ToUInt32(ba, i * DataSize); } public UIntX(decimal value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; if (value < 0) { var n = -new UIntX(-value, DataBitLength); 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 UIntX(double value, int bitLength) : this((decimal) value, bitLength) { } public UIntX(float value, int bitLength) : this((decimal) value, bitLength) { } public UIntX(short value, int bitLength) : this((int) value, bitLength) { } public UIntX(int value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = (uint) value; } public UIntX(long value, int bitLength) : this((ulong) value, bitLength) { } public UIntX(sbyte value, int bitLength) : this((int) value, bitLength) { } public UIntX(ushort value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = value; } public UIntX(uint value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = value; } public UIntX(ulong value, int bitLength) { DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; data[0] = (uint) ((value >> 32) & 0xffffffff); data[1] = (uint) (value & 0xffffffff); } public UIntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength) { } public UIntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength) { } public UIntX(byte[] value, int bitLength) { if (value == null) throw new ArgumentNullException("value"); DataBitLength = bitLength; DataLength = DataBitLength >> 5; var minSize = value.Length / DataSize; var byteCount = value.Length; var isNegative = 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, minSize)]; 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 UIntX(uint[] array, int bitLength) { if (array == null) throw new Exception("Array cannot be null."); DataBitLength = bitLength; DataLength = DataBitLength >> 5; data = new uint[DataLength]; var ba = new byte[4]; 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); } } protected UIntX(SerializationInfo info, StreamingContext context) { SInfo = info; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public UIntX MaxValue => (One << DataBitLength) - 1; public bool IsOne => this == One; public bool IsEven => this % 2 == 0; public bool IsZero { get { for (var i = 0; i < data.Length; i++) if (data[i] != 0) return false; return true; } } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public int CompareTo(UIntX 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 (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 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) throw new OverflowException(); return data[0]; } public bool Equals(UIntX obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; if (data.Length != obj.data.Length) 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', 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); } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Bits", DataBitLength); info.AddValue("Data", data, typeof(uint[])); } public void OnDeserialization(object sender) { if (SInfo == null) return; DataBitLength = SInfo.GetInt32("Bits"); if (DataBitLength != 0) { DataLength = DataBitLength >> 5; var array = (uint[]) SInfo.GetValue("Data", typeof(uint[])); if (array == null) throw new Exception("Array cannot be null."); if (array.Length != DataLength) Array.Resize(ref array, DataLength); data = new uint[DataLength]; var ba = new byte[4]; for (var i = 0; i < DataLength; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); data[i] = BitConverter.ToUInt32(ba, 0); } } } 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() { var hash = 0x811c9dc5; for (var i = 0; i < DataLength; i++) { hash ^= ((hash << 13) | (hash >> 19)) ^ data[i]; hash *= 0x1000193; } return (int) hash; } 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); } 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 (IsZero) return "0"; var a = new UIntX(this, DataBitLength); var biRadix = new UIntX(radix, DataBitLength); 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))); return result; } 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 UIntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static UIntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static UIntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static UIntX 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 UIntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out UIntX 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 UIntX result) { result = new UIntX(0, DataBitLength); if (digits == null) return false; var multiplier = new UIntX(1, DataBitLength); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int) digits[idx]; if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else return false; if (d >= radix) return false; result += multiplier * d; multiplier *= radix; } if (digits[0] == '-') result = -result; return true; } public static int Compare(UIntX left, object right) { if (right is UIntX) return Compare(left, (UIntX) right); if (right is bool) return Compare(left, new UIntX((bool) right, DataBitLength)); if (right is byte) return Compare(left, new UIntX((byte) right, DataBitLength)); if (right is char) return Compare(left, new UIntX((char) right, DataBitLength)); if (right is decimal) return Compare(left, new UIntX((decimal) right, DataBitLength)); if (right is double) return Compare(left, new UIntX((double) right, DataBitLength)); if (right is short) return Compare(left, new UIntX((short) right, DataBitLength)); if (right is int) return Compare(left, new UIntX((int) right, DataBitLength)); if (right is long) return Compare(left, new UIntX((long) right, DataBitLength)); if (right is sbyte) return Compare(left, new UIntX((sbyte) right, DataBitLength)); if (right is float) return Compare(left, new UIntX((float) right, DataBitLength)); if (right is ushort) return Compare(left, new UIntX((ushort) right, DataBitLength)); if (right is uint) return Compare(left, new UIntX((uint) right, DataBitLength)); if (right is ulong) return Compare(left, new UIntX((ulong) right, DataBitLength)); var bytes = right as byte[]; if (bytes != null && bytes.Length != 64) return Compare(left, new UIntX(bytes, DataBitLength)); if (right is Guid) return Compare(left, new UIntX((Guid) right, DataBitLength)); throw new ArgumentException(); } public static int Compare(UIntX left, UIntX right) { if (left.data.Length < right.data.Length) return -1; if (ReferenceEquals(left, right)) return 0; if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); if (left > right) return 1; if (left == right) return 0; return -1; } public static implicit operator UIntX(bool value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(byte value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(char value) { return new UIntX(value, DataBitLength); } public static explicit operator UIntX(decimal value) { return new UIntX(value, DataBitLength); } public static explicit operator UIntX(double value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(short value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(int value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(long value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(sbyte value) { return new UIntX(value, DataBitLength); } public static explicit operator UIntX(float value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(ushort value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(uint value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(ulong value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(BigInteger value) { return new UIntX(value, DataBitLength); } public static implicit operator UIntX(BigDecimal value) { return new UIntX(value, DataBitLength); } public static explicit operator bool(UIntX value) { return (byte) value.data[0] != 0; } public static explicit operator byte(UIntX value) { return (byte) value.data[0]; } public static explicit operator char(UIntX value) { return (char) (ushort) value.data[0]; } public static explicit operator decimal(UIntX value) { return new decimal((int) value.data[0], (int) value.data[1], (int) value.data[2], false, 0); } public static explicit operator double(UIntX value) { 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(UIntX value) { 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(UIntX value) { return (short) (int) value.data[0]; } public static explicit operator int(UIntX value) { return (int) value.data[0]; } public static explicit operator long(UIntX value) { if (value.data[1] != 0) return (long) (((ulong) value.data[1] << 32) | value.data[0]); return value.data[0]; } public static explicit operator uint(UIntX value) { return value.data[0]; } public static explicit operator ushort(UIntX value) { return (ushort) value.data[0]; } public static explicit operator ulong(UIntX value) { if (value.data[1] != 0) return ((ulong) value.data[1] << 32) | value.data[0]; return value.data[0]; } public static explicit operator BigInteger(UIntX value) { return new BigInteger(value.ToByteArray()); } public static bool operator >(UIntX left, UIntX right) { if (left.data.Length > right.data.Length) return false; if (left.data.Length < right.data.Length) return true; if (ReferenceEquals(left, null)) throw new ArgumentNullException("left"); if (ReferenceEquals(right, null)) throw new ArgumentNullException("right"); for (var i = 0; i < DataLength; i++) if (left.data[i] != right.data[i]) return left.data[i] > right.data[i]; return false; } public static bool operator <(UIntX left, UIntX right) { return Compare(left, right) < 0; } public static bool operator >=(UIntX left, UIntX right) { return Compare(left, right) >= 0; } public static bool operator <=(UIntX left, UIntX right) { return Compare(left, right) <= 0; } public static bool operator !=(UIntX left, UIntX right) { return Compare(left, right) != 0; } public static bool operator ==(UIntX left, UIntX right) { if (ReferenceEquals(left, right)) return true; if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) return false; return left.Equals(right); } public static UIntX operator +(UIntX value) { return value; } public static UIntX operator ~(UIntX value) { var da = new uint[DataLength]; for (var idx = 0; idx < DataLength; idx++) da[idx] = ~value.data[idx]; return new UIntX(da, DataBitLength); } public static UIntX operator -(UIntX value) { return Negate(value); } public static UIntX operator ++(UIntX value) { return value + 1; } public static UIntX operator --(UIntX value) { return value - 1; } public static UIntX Negate(UIntX value) { for (var i = 0; i < DataLength; i++) value.data[i] = ~value.data[i]; return new UIntX(value, DataBitLength); } public static UIntX operator +(UIntX left, UIntX right) { var dl = left.data.Length > right.data.Length ? left.data.Length : right.data.Length; var result = new uint[dl]; long carry = 0; for (var i = 0; i < dl; 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 UIntX(result, DataBitLength); } public static UIntX operator -(UIntX left, UIntX 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 < da.Length - 1; i++) { var diff = left.data[i] - (long) right.data[i] - carry; da[i] = (uint) (diff & DigitsArray.AllBits); carry = diff < 0 ? 1 : 0; } return new UIntX(da, DataBitLength); } public static UIntX Add(UIntX left, UIntX right) { return left + right; } public static UIntX Subtract(UIntX left, UIntX right) { return left - right; } public static UIntX Divide(UIntX dividend, UIntX divisor) { return DivRem(dividend, divisor, out var integer); } public static void Divide(UIntX dividend, UIntX divisor, out UIntX remainder, out UIntX quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.data, divisor.data, out var quo, out var rem); remainder = new UIntX(rem, DataBitLength); quotient = new UIntX(quo, DataBitLength); } public static UIntX DivRem(UIntX dividend, UIntX divisor, out UIntX remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.data, divisor.data, out var quotient, out var rem); remainder = new UIntX(rem, DataBitLength); return new UIntX(quotient, DataBitLength); } 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 UIntX Remainder(UIntX dividend, UIntX divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static UIntX Max(UIntX left, UIntX right) { return left.CompareTo(right) < 0 ? right : left; } public static UIntX Min(UIntX left, UIntX right) { return left.CompareTo(right) <= 0 ? left : right; } public static UIntX operator %(UIntX dividend, UIntX divisor) { return Remainder(dividend, divisor); } public static UIntX operator /(UIntX dividend, UIntX 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; } public byte[] ToByteArray(int length) { if (length <= 0 || length > data.Length * DataSize) throw new ArgumentException($"Length {length} out of range length > 0 or length <= {data.Length * DataSize}"); var ba = new byte[length]; Buffer.BlockCopy(data, 0, ba, 0, length); return ba; } public static UIntX Multiply(UIntX left, UIntX right) { 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 UIntX(mulInts, DataBitLength); } public static UIntX operator *(UIntX left, UIntX right) { return Multiply(left, right); } public static UIntX operator >>(UIntX value, int shift) { if (value == Zero) return Zero; var xd = (uint[]) value.data.Clone(); 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 UIntX(xd, DataBitLength); } public static UIntX operator <<(UIntX value, int shift) { if (value == Zero) return Zero; var digitShift = shift / 32; var smallShift = shift - digitShift * 32; var xd = (uint[]) value.data.Clone(); 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 UIntX(zd, DataBitLength); } public static UIntX operator |(UIntX left, UIntX right) { if (left == 0) return right; if (right == 0) return left; var z = new uint[Math.Max(left.data.Length, right.data.Length)]; for (var i = 0; i < z.Length; i++) { var xu = i < left.data.Length ? left.data[i] : 0U; var yu = i < right.data.Length ? right.data[i] : 0U; z[i] = xu | yu; } return new UIntX(z, DataBitLength); } public static UIntX operator ^(UIntX left, UIntX right) { var z = new uint[Math.Max(left.data.Length, right.data.Length)]; for (var i = 0; i < z.Length; i++) { var xu = i < left.data.Length ? left.data[i] : 0U; var yu = i < right.data.Length ? right.data[i] : 0U; z[i] = xu ^ yu; } return new UIntX(z, DataBitLength); } public static UIntX operator &(UIntX left, UIntX right) { if (left == 0 || right == 0) return Zero; var z = new uint[Math.Max(left.data.Length, right.data.Length)]; for (var i = 0; i < z.Length; i++) { var xu = i < left.data.Length ? left.data[i] : 0U; var yu = i < right.data.Length ? right.data[i] : 0U; z[i] = xu & yu; } return new UIntX(z, DataBitLength); } private class UIntXConverter : 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 UIntX("0", DataBitLength); } 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); } } }
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(UIntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class UIntX : IComparable<UIntX>, IComparable, IEquatable<UIntX>, IConvertible, IFormattable, ISerializable
{
    private const    int               DataSize = sizeof(uint);
    private static   int               DataBitLength;
    private static   int               DataLength;
    public static    UIntX             Zero = new UIntX("0",  32);
    public static    UIntX             Ten  = new UIntX("10", 32);
    public static    UIntX             One  = new UIntX("1",  32);
    private readonly SerializationInfo SInfo;
    private          uint[]            data;
    public UIntX(UIntX value, int bitLength)
    {
        if (value == null)
            return;
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        value.data.CopyTo(data, 0);
    }
    public UIntX(string value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        if (!TryParse(value, out var result))
            throw new Exception("TryParse Failed.");
        data = new uint[DataLength];
        result.data.CopyTo(data, 0);
    }
    public UIntX(byte value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = value;
    }
    public UIntX(bool value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = (uint) (value ? 1 : 0);
    }
    public UIntX(char value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = value;
    }
    public UIntX(BigDecimal value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        var ba = value.UnscaledValue.ToByteArray();
        data = new uint[DataLength];
        for (var i = 0; i < DataLength; i++)
            data[i] = BitConverter.ToUInt32(ba, i * DataSize);
    }
    public UIntX(decimal value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        if (value < 0)
        {
            var n = -new UIntX(-value, DataBitLength);
            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 UIntX(double value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public UIntX(float value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public UIntX(short value, int bitLength) : this((int) value, bitLength)
    {
    }
    public UIntX(int value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = (uint) value;
    }
    public UIntX(long value, int bitLength) : this((ulong) value, bitLength)
    {
    }
    public UIntX(sbyte value, int bitLength) : this((int) value, bitLength)
    {
    }
    public UIntX(ushort value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = value;
    }
    public UIntX(uint value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = value;
    }
    public UIntX(ulong value, int bitLength)
    {
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        data[0]       = (uint) ((value >> 32) & 0xffffffff);
        data[1]       = (uint) (value         & 0xffffffff);
    }
    public UIntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public UIntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public UIntX(byte[] value, int bitLength)
    {
        if (value == null)
            throw new ArgumentNullException("value");
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        var minSize        = value.Length / DataSize;
        var byteCount      = value.Length;
        var isNegative     = 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, minSize)];
        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 UIntX(uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        DataBitLength = bitLength;
        DataLength    = DataBitLength >> 5;
        data          = new uint[DataLength];
        var ba = new byte[4];
        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);
        }
    }
    protected UIntX(SerializationInfo info, StreamingContext context)
    {
        SInfo = info;
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => ToString();
    public UIntX MaxValue => (One << DataBitLength) - 1;
    public bool  IsOne    => this     == One;
    public bool  IsEven   => this % 2 == 0;
    public bool IsZero
    {
        get
        {
            for (var i = 0; i < data.Length; i++)
                if (data[i] != 0)
                    return false;
            return true;
        }
    }
    int IComparable.CompareTo(object obj)
    {
        return Compare(this, obj);
    }
    public int CompareTo(UIntX 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 (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 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)
            throw new OverflowException();
        return data[0];
    }
    public bool Equals(UIntX obj)
    {
        if (ReferenceEquals(obj, null))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (data.Length != obj.data.Length)
            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', 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);
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bits", DataBitLength);
        info.AddValue("Data", data, typeof(uint[]));
    }
    public void OnDeserialization(object sender)
    {
        if (SInfo == null)
            return;
        DataBitLength = SInfo.GetInt32("Bits");
        if (DataBitLength != 0)
        {
            DataLength = DataBitLength >> 5;
            var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
            if (array == null)
                throw new Exception("Array cannot be null.");
            if (array.Length != DataLength)
                Array.Resize(ref array, DataLength);
            data = new uint[DataLength];
            var ba = new byte[4];
            for (var i = 0; i < DataLength; i++)
            {
                Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
                data[i] = BitConverter.ToUInt32(ba, 0);
            }
        }
    }
    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()
    {
        var hash = 0x811c9dc5;
        for (var i = 0; i < DataLength; i++)
        {
            hash ^= ((hash << 13) | (hash >> 19)) ^ data[i];
            hash *= 0x1000193;
        }
        return (int) hash;
    }
    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);
    }
    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 (IsZero)
            return "0";
        var          a       = new UIntX(this,  DataBitLength);
        var          biRadix = new UIntX(radix, DataBitLength);
        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)));
        return result;
    }
    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 UIntX Parse(string value)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
    }
    public static UIntX Parse(string value, NumberStyles style)
    {
        return Parse(value, style, NumberFormatInfo.CurrentInfo);
    }
    public static UIntX Parse(string value, IFormatProvider provider)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
    }
    public static UIntX 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 UIntX result)
    {
        return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
    }
    public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out UIntX 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 UIntX result)
    {
        result = new UIntX(0, DataBitLength);
        if (digits == null)
            return false;
        var multiplier = new UIntX(1, DataBitLength);
        digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
        var nDigits = digits[0] == '-' ? 1 : 0;
        for (var idx = digits.Length - 1; idx >= nDigits; idx--)
        {
            var d = (int) digits[idx];
            if (d >= '0' && d <= '9')
                d -= '0';
            else if (d >= 'A' && d <= 'Z')
                d = d - 'A' + 10;
            else
                return false;
            if (d >= radix)
                return false;
            result     += multiplier * d;
            multiplier *= radix;
        }
        if (digits[0] == '-')
            result = -result;
        return true;
    }
    public static int Compare(UIntX left, object right)
    {
        if (right is UIntX)
            return Compare(left, (UIntX) right);
        if (right is bool)
            return Compare(left, new UIntX((bool) right, DataBitLength));
        if (right is byte)
            return Compare(left, new UIntX((byte) right, DataBitLength));
        if (right is char)
            return Compare(left, new UIntX((char) right, DataBitLength));
        if (right is decimal)
            return Compare(left, new UIntX((decimal) right, DataBitLength));
        if (right is double)
            return Compare(left, new UIntX((double) right, DataBitLength));
        if (right is short)
            return Compare(left, new UIntX((short) right, DataBitLength));
        if (right is int)
            return Compare(left, new UIntX((int) right, DataBitLength));
        if (right is long)
            return Compare(left, new UIntX((long) right, DataBitLength));
        if (right is sbyte)
            return Compare(left, new UIntX((sbyte) right, DataBitLength));
        if (right is float)
            return Compare(left, new UIntX((float) right, DataBitLength));
        if (right is ushort)
            return Compare(left, new UIntX((ushort) right, DataBitLength));
        if (right is uint)
            return Compare(left, new UIntX((uint) right, DataBitLength));
        if (right is ulong)
            return Compare(left, new UIntX((ulong) right, DataBitLength));
        var bytes = right as byte[];
        if (bytes != null && bytes.Length != 64)
            return Compare(left, new UIntX(bytes, DataBitLength));
        if (right is Guid)
            return Compare(left, new UIntX((Guid) right, DataBitLength));
        throw new ArgumentException();
    }
    public static int Compare(UIntX left, UIntX right)
    {
        if (left.data.Length < right.data.Length)
            return -1;
        if (ReferenceEquals(left, right))
            return 0;
        if (ReferenceEquals(left, null))
            throw new ArgumentNullException("left");
        if (ReferenceEquals(right, null))
            throw new ArgumentNullException("right");
        if (left > right) return 1;
        if (left == right) return 0;
        return -1;
    }
    public static implicit operator UIntX(bool value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(byte value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(char value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static explicit operator UIntX(decimal value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static explicit operator UIntX(double value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(short value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(int value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(long value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(sbyte value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static explicit operator UIntX(float value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(ushort value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(uint value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(ulong value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(BigInteger value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static implicit operator UIntX(BigDecimal value)
    {
        return new UIntX(value, DataBitLength);
    }
    public static explicit operator bool(UIntX value)
    {
        return (byte) value.data[0] != 0;
    }
    public static explicit operator byte(UIntX value)
    {
        return (byte) value.data[0];
    }
    public static explicit operator char(UIntX value)
    {
        return (char) (ushort) value.data[0];
    }
    public static explicit operator decimal(UIntX value)
    {
        return new decimal((int) value.data[0], (int) value.data[1], (int) value.data[2], false, 0);
    }
    public static explicit operator double(UIntX value)
    {
        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(UIntX value)
    {
        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(UIntX value)
    {
        return (short) (int) value.data[0];
    }
    public static explicit operator int(UIntX value)
    {
        return (int) value.data[0];
    }
    public static explicit operator long(UIntX value)
    {
        if (value.data[1] != 0)
            return (long) (((ulong) value.data[1] << 32) | value.data[0]);
        return value.data[0];
    }
    public static explicit operator uint(UIntX value)
    {
        return value.data[0];
    }
    public static explicit operator ushort(UIntX value)
    {
        return (ushort) value.data[0];
    }
    public static explicit operator ulong(UIntX value)
    {
        if (value.data[1] != 0)
            return ((ulong) value.data[1] << 32) | value.data[0];
        return value.data[0];
    }
    public static explicit operator BigInteger(UIntX value)
    {
        return new BigInteger(value.ToByteArray());
    }
    public static bool operator >(UIntX left, UIntX right)
    {
        if (left.data.Length > right.data.Length)
            return false;
        if (left.data.Length < right.data.Length)
            return true;
        if (ReferenceEquals(left, null))
            throw new ArgumentNullException("left");
        if (ReferenceEquals(right, null))
            throw new ArgumentNullException("right");
        for (var i = 0; i < DataLength; i++)
            if (left.data[i] != right.data[i])
                return left.data[i] > right.data[i];
        return false;
    }
    public static bool operator <(UIntX left, UIntX right)
    {
        return Compare(left, right) < 0;
    }
    public static bool operator >=(UIntX left, UIntX right)
    {
        return Compare(left, right) >= 0;
    }
    public static bool operator <=(UIntX left, UIntX right)
    {
        return Compare(left, right) <= 0;
    }
    public static bool operator !=(UIntX left, UIntX right)
    {
        return Compare(left, right) != 0;
    }
    public static bool operator ==(UIntX left, UIntX right)
    {
        if (ReferenceEquals(left, right))
            return true;
        if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
            return false;
        return left.Equals(right);
    }
    public static UIntX operator +(UIntX value)
    {
        return value;
    }
    public static UIntX operator ~(UIntX value)
    {
        var da = new uint[DataLength];
        for (var idx = 0; idx < DataLength; idx++)
            da[idx] = ~value.data[idx];
        return new UIntX(da, DataBitLength);
    }
    public static UIntX operator -(UIntX value)
    {
        return Negate(value);
    }
    public static UIntX operator ++(UIntX value)
    {
        return value + 1;
    }
    public static UIntX operator --(UIntX value)
    {
        return value - 1;
    }
    public static UIntX Negate(UIntX value)
    {
        for (var i = 0; i < DataLength; i++)
            value.data[i] = ~value.data[i];
        return new UIntX(value, DataBitLength);
    }
    public static UIntX operator +(UIntX left, UIntX right)
    {
        var  dl     = left.data.Length > right.data.Length ? left.data.Length : right.data.Length;
        var  result = new uint[dl];
        long carry  = 0;
        for (var i = 0; i < dl; 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 UIntX(result, DataBitLength);
    }
    public static UIntX operator -(UIntX left, UIntX 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 < da.Length - 1; i++)
        {
            var diff = left.data[i] - (long) right.data[i] - carry;
            da[i] = (uint) (diff & DigitsArray.AllBits);
            carry = diff < 0 ? 1 : 0;
        }
        return new UIntX(da, DataBitLength);
    }
    public static UIntX Add(UIntX left, UIntX right)
    {
        return left + right;
    }
    public static UIntX Subtract(UIntX left, UIntX right)
    {
        return left - right;
    }
    public static UIntX Divide(UIntX dividend, UIntX divisor)
    {
        return DivRem(dividend, divisor, out var integer);
    }
    public static void Divide(UIntX dividend, UIntX divisor, out UIntX remainder, out UIntX quotient)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.data, divisor.data, out var quo, out var rem);
        remainder = new UIntX(rem, DataBitLength);
        quotient  = new UIntX(quo, DataBitLength);
    }
    public static UIntX DivRem(UIntX dividend, UIntX divisor, out UIntX remainder)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.data, divisor.data, out var quotient, out var rem);
        remainder = new UIntX(rem, DataBitLength);
        return new UIntX(quotient, DataBitLength);
    }
    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 UIntX Remainder(UIntX dividend, UIntX divisor)
    {
        DivRem(dividend, divisor, out var remainder);
        return remainder;
    }
    public static UIntX Max(UIntX left, UIntX right)
    {
        return left.CompareTo(right) < 0 ? right : left;
    }
    public static UIntX Min(UIntX left, UIntX right)
    {
        return left.CompareTo(right) <= 0 ? left : right;
    }
    public static UIntX operator %(UIntX dividend, UIntX divisor)
    {
        return Remainder(dividend, divisor);
    }
    public static UIntX operator /(UIntX dividend, UIntX 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;
    }
    public byte[] ToByteArray(int length)
    {
        if (length <= 0 || length > data.Length * DataSize)
            throw new ArgumentException($"Length {length} out of range length > 0 or length <= {data.Length * DataSize}");
        var ba = new byte[length];
        Buffer.BlockCopy(data, 0, ba, 0, length);
        return ba;
    }
    public static UIntX Multiply(UIntX left, UIntX right)
    {
        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 UIntX(mulInts, DataBitLength);
    }
    public static UIntX operator *(UIntX left, UIntX right)
    {
        return Multiply(left, right);
    }
    public static UIntX operator >>(UIntX value, int shift)
    {
        if (value == Zero)
            return Zero;
        var xd          = (uint[]) value.data.Clone();
        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 UIntX(xd, DataBitLength);
    }
    public static UIntX operator <<(UIntX value, int shift)
    {
        if (value == Zero)
            return Zero;
        var digitShift = shift / 32;
        var smallShift = shift - digitShift * 32;
        var xd         = (uint[]) value.data.Clone();
        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 UIntX(zd, DataBitLength);
    }
    public static UIntX operator |(UIntX left, UIntX right)
    {
        if (left == 0)
            return right;
        if (right == 0)
            return left;
        var z = new uint[Math.Max(left.data.Length, right.data.Length)];
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.data.Length ? left.data[i] : 0U;
            var yu = i < right.data.Length ? right.data[i] : 0U;
            z[i] = xu | yu;
        }
        return new UIntX(z, DataBitLength);
    }
    public static UIntX operator ^(UIntX left, UIntX right)
    {
        var z = new uint[Math.Max(left.data.Length, right.data.Length)];
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.data.Length ? left.data[i] : 0U;
            var yu = i < right.data.Length ? right.data[i] : 0U;
            z[i] = xu ^ yu;
        }
        return new UIntX(z, DataBitLength);
    }
    public static UIntX operator &(UIntX left, UIntX right)
    {
        if (left == 0 || right == 0)
            return Zero;
        var z = new uint[Math.Max(left.data.Length, right.data.Length)];
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.data.Length ? left.data[i] : 0U;
            var yu = i < right.data.Length ? right.data[i] : 0U;
            z[i] = xu & yu;
        }
        return new UIntX(z, DataBitLength);
    }
    private class UIntXConverter : 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 UIntX("0", DataBitLength);
        }
        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);
        }
    }
}