BigIntX.cs

Variable Bit Width Big Unsigned or Signed Integer 32,64,128,256,512,1024,204,4096 Bit. (Experimental)

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

FNV1aX.cs

FNV1a 128…1024Bit Hashing BigInteger

using System;
using System.Numerics;
using System.Security.Cryptography;
using static System.Globalization.CultureInfo;
using static System.Globalization.NumberStyles;
using static System.Numerics.BigInteger;
[Serializable]
public class FNV1aX : HashAlgorithm
{
    private static int        _bitWidth;
    private        BigInteger _h1;
    public         BigInteger _mod;
    public         BigInteger _offset;
    private        BigInteger _prime;
    public FNV1aX(int bitWidth = 128)
    {
        _bitWidth = bitWidth;
        SetBitWidthPom(bitWidth);
        _h1 = _offset;
    }
    public override int HashSize => _bitWidth;
    private void SetBitWidthPom(int bitWidth)
    {
        switch (bitWidth)
        {
            //case 32:
            //    _prime  = Parse("1000193", AllowHexSpecifier, InvariantCulture);
            //    _offset = Parse("811c9dc5", AllowHexSpecifier, InvariantCulture);
            //    _mod    = Parse("10000000", AllowHexSpecifier, InvariantCulture);
            //    break;
            //case 64:
            //    _prime  = Parse("100000001B3", AllowHexSpecifier, InvariantCulture);
            //    _offset = Parse("CBF29CE484222325", AllowHexSpecifier, InvariantCulture);
            //    _mod    = Parse("1000000000000000", AllowHexSpecifier, InvariantCulture);
            //    break;
            case 128:
                _prime = Parse("0000000001000000000000000000013B", AllowHexSpecifier, InvariantCulture);
                _offset = Parse("6c62272e07bb014262b821756295c58d", AllowHexSpecifier, InvariantCulture);
                _mod = Parse("10000000000000000000000000000000", AllowHexSpecifier, InvariantCulture);
                break;
            case 256:
                _prime  = Parse("0000000000000000000001000000000000000000000000000000000000000163", AllowHexSpecifier, InvariantCulture);
                _offset = Parse("dd268dbcaac550362d98c384c4e576ccc8b1536847b6bbb31023b4c8caee0535", AllowHexSpecifier, InvariantCulture);
                _mod    = Parse("1000000000000000000000000000000000000000000000000000000000000000", AllowHexSpecifier, InvariantCulture);
                break;
            case 512:
                _prime = Parse("00000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000157",
                    AllowHexSpecifier, InvariantCulture);
                _offset = Parse("b86db0b1171f4416dca1e50f309990acac87d059c90000000000000000000d21e948f68a34c192f62ea79bc942dbe7ce182036415f56e34bac982aac4afe9fd9",
                    AllowHexSpecifier, InvariantCulture);
                _mod = Parse("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", AllowHexSpecifier,
                    InvariantCulture);
                break;
            case 1024:
                _prime = Parse(
                    "000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018D",
                    AllowHexSpecifier, InvariantCulture);
                _offset = Parse(
                    "0000000000000000005f7a76758ecc4d32e56d5a591028b74b29fc4223fdada16c3bf34eda3674da9a21d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c6d7eb6e73802734510a555f256cc005ae556bde8cc9c6a93b21aff4b16c71ee90b3",
                    AllowHexSpecifier, InvariantCulture);
                _mod = Parse(
                    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
                    AllowHexSpecifier, InvariantCulture);
                break;
        }
    }
    public override void Initialize()
    {
        _h1 = _offset;
    }
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        Hash(bytes, ibStart, cbSize);
    }
    private unsafe void Hash(byte[] bytes, int ibStart, int cbSize)
    {
        if (bytes == null)
            return;
        if (ibStart >= bytes.Length || cbSize > bytes.Length)
            return;
        fixed (byte* pb = bytes)
        {
            var nb = pb + ibStart;
            while (cbSize >= 8)
            {
                _h1    ^= *(ulong*) nb;
                _h1    *= _prime;
                _h1    %= _mod;
                nb     += 8;
                cbSize -= 8;
            }
            switch (cbSize & 7)
            {
                case 7:
                    _h1 ^= *(ulong*) (nb + 6);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 6;
                case 6:
                    _h1 ^= *(ulong*) (nb + 5);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 5;
                case 5:
                    _h1 ^= *(ulong*) (nb + 4);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 4;
                case 4:
                    _h1 ^= *(ulong*) (nb + 3);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 3;
                case 3:
                    _h1 ^= *(ulong*) (nb + 2);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 2;
                case 2:
                    _h1 ^= *(ulong*) (nb + 1);
                    _h1 *= _prime;
                    _h1 %= _mod;
                    goto case 1;
                case 1:
                    _h1 ^= *nb;
                    _h1 *= _prime;
                    _h1 %= _mod;
                    break;
            }
        }
    }
    protected override byte[] HashFinal()
    {
        return _h1.ToByteArray();
    }
}

RandomBigInt.cs

BigInteger Random Number Generator

Updated: July-10,2021

using System;
using System.Numerics;
using System.Security.Cryptography;
public struct RandomBigInt
{
    private readonly RNGCryptoServiceProvider _crng;
    private          int                      _maxByteWidth;
    private          int                      _bitWidth;
    public RandomBigInt(int bitWidth)
    {
        MaxValue          = BigIntegerHelper.GetMaxValueBitWidth(bitWidth);
        MaxValue.BitWidth = bitWidth;
        _maxByteWidth     = bitWidth >> 3;
        OddsOnly          = false;
        Unsigned          = false;
        _crng             = new RNGCryptoServiceProvider();
        _bitWidth         = bitWidth;
    }
    public bool OddsOnly;
    public bool Unsigned;
    public int BitWidth
    {
        get => _bitWidth;
        set
        {
            _bitWidth         = value;
            MaxValue          = BigIntegerHelper.GetMaxValueBitWidth(_bitWidth);
            MaxValue.BitWidth = _bitWidth;
            _maxByteWidth     = _bitWidth >> 3;
        }
    }
    public BigDecimal MaxValue;
    public bool NextBool()
    {
        return Sample() < .5;
    }
    public BigInteger Next()
    {
        return Internal();
    }
    public BigInteger Next(BigInteger minValue, BigInteger maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        return ((BigInteger)(Sample() * (maxValue - minValue)) + minValue) & (BigInteger)MaxValue;
    }
    public BigInteger Next(BigInteger maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        return (BigInteger)(Sample() * maxValue) & (BigInteger)MaxValue;
    }
    public unsafe double NextDouble()
    {
        var buf = new byte[8];
        GetBytes(buf);
        fixed (byte* ptr = buf)
        {
            return *(ulong*)ptr * (1.0 / ulong.MaxValue) * ulong.MaxValue;
        }
    }
    public BigRational NextBigRational()
    {
        return new BigRational(Internal(), Internal());
    }
    public decimal NextDecimal()
    {
        return new decimal((int)Next(int.MaxValue), (int)Next(int.MaxValue), (int)Next(int.MaxValue), NextBool(), (byte)Next(255));
    }
    public BigDecimal NextBigDecimal()
    {
        return Sample();
    }
    public byte[] GetNextByteArray(int size)
    {
        var ba = new byte[size];
        _crng.GetBytes(ba);
        return ba;
    }
    public char[] GetNextCharArray(int size)
    {
        var xbc = new byte[1];
        var ca  = new char[size];
        var ptr = 0;
        do
        {
            _crng.GetBytes(xbc);
            var c = xbc[0];
            if (c >= 0x20 && c <= 0x7F)
                ca[ptr++] = (char)c;
        } while (ptr < size);
        return ca;
    }
    public char NextChar()
    {
        var xbc = new byte[1];
        while (true)
        {
            _crng.GetBytes(xbc);
            var c = xbc[0];
            if (c >= 0x20 && c <= 0x7F)
                return (char)c;
        }
    }
    public bool[] GetNextBoolArrayLimit(int size)
    {
        var        ba = new bool[size];
        const uint ll = uint.MaxValue >> 1;
        for (var i = 0; i < size; ++i)
            ba[i] = Next(0, uint.MaxValue) > ll;
        return ba;
    }
    public byte[] GetNextByteArrayLimit(int size, ulong minValue, ulong maxValue)
    {
        var ba = new byte[size];
        for (var i = 0; i < size; ++i)
            ba[i] = (byte)Next(minValue, maxValue);
        return ba;
    }
    public string GetRandomString(int Len)
    {
        return new string(GetNextCharArray(Len));
    }
    public string GetRandomString(int minLen, int maxLen)
    {
        return minLen == maxLen ? new string(GetNextCharArray(minLen)) : new string(GetNextCharArray((int)Next((uint)minLen, (uint)maxLen)));
    }
    private BigDecimal Sample()
    {
        var i = Internal();
        var s = i * (BigDecimal.One / MaxValue);
        if (s.IsZero)
            throw new Exception($"Sample is zero. Check BigDecimal BitWidth {MaxValue.BitWidth} Equals BitWidth {_bitWidth}");
        return s;
    }
    public void GetBytes(byte[] data)
    {
        if (data == null)
            throw new ArgumentException("The buffer cannot be null.");
        _crng.GetBytes(data);
    }
    private BigInteger Internal()
    {
        if (Unsigned)
        {
            var buffer = new byte[_maxByteWidth + 1];
            _crng.GetBytes(buffer);
            buffer[_maxByteWidth] = 0;
            var n = new BigInteger(buffer);
            return !OddsOnly ? n : n | 1;
        }
        else
        {
            var buffer = new byte[_maxByteWidth];
            _crng.GetBytes(buffer);
            var n = new BigInteger(buffer);
            return !OddsOnly ? n : n | 1;
        }
    }
    public void NextBytes(byte[] buffer)
    {
        if (buffer == null)
            throw new ArgumentNullException("The buffer cannot be null.");
        for (var index = 0; index < buffer.Length; ++index)
            buffer[index] = (byte)(Sample() * byte.MaxValue + 1);
    }
    public void GetNonZeroBytes(byte[] buffer)
    {
        if (buffer == null)
            throw new ArgumentNullException("The buffer cannot be null.");
        var index = 0;
        do
        {
            var v = (byte)(Sample() * byte.MaxValue + 1);
            if (v > 0)
            {
                buffer[index] = v;
                index++;
            }
        } while (index < buffer.Length);
    }
    public string[] GetUniqueStringArray(int numberItems, int minLen, int maxLen)
    {
        var sl = new MSet15<string>();
        do
        {
            sl.Add(GetRandomString(minLen, maxLen));
        } while (sl.Count < numberItems);
        return sl.ToArray();
    }
}

RandomX.cs

A Fast Random Number Generator Based on BigInteger

using System;
using System.Numerics;
using System.Security.Cryptography;
public struct RandomX
{
    private readonly RNGCryptoServiceProvider _crng;
    private          int                      _maxByteWidth;
    private          int                      _bitWidth;
    public RandomX(int bitWidth)
    {
        MaxValue      = BigIntegerHelper.GetMaxValueBitWidth(bitWidth);
        _maxByteWidth = bitWidth >> 3;
        OddsOnly      = false;
        Unsigned      = false;
        _crng         = new RNGCryptoServiceProvider();
        _bitWidth     = bitWidth;
    }
    public bool OddsOnly;
    public bool Unsigned;
    public int BitWidth
    {
        get => _bitWidth;
        set
        {
            _bitWidth     = value;
            MaxValue      = BigIntegerHelper.GetMaxValueBitWidth(_bitWidth);
            _maxByteWidth = _bitWidth >> 3;
        }
    }
    public BigInteger MaxValue;
    public bool NextBool()
    {
        return Sample() < .5;
    }
    public char NextChar(char maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (char) (Sample() * maxValue);
    }
    public char NextChar(char minValue, char maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (char) Next(minValue, maxValue);
    }
    public sbyte NextInt8()
    {
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 8)
            BitWidth = 8;
        return (sbyte) Internal();
    }
    public sbyte NextInt8(sbyte maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 8)
            BitWidth = 8;
        return (sbyte) (Sample() * maxValue);
    }
    public sbyte NextInt8(sbyte minValue, sbyte maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 8)
            BitWidth = 8;
        return (sbyte) Next(minValue, maxValue);
    }
    public byte NextUInt8()
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 8)
            BitWidth = 8;
        var n = Internal();
        return (byte) n;
    }
    public byte NextUInt8(byte maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 8)
            BitWidth = 8;
        return (byte) (Sample() * maxValue);
    }
    public byte NextUInt8(byte minValue, byte maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 8)
            BitWidth = 8;
        return (byte) Next(minValue, maxValue);
    }
    public short NextInt16()
    {
        if (_bitWidth < 16)
            BitWidth = 16;
        if (Unsigned)
            Unsigned = false;
        return (short) Internal();
    }
    public short NextInt16(short maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (short) (Sample() * maxValue);
    }
    public short NextInt16(short minValue, short maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (short) Next(minValue, maxValue);
    }
    public ushort NextUInt16()
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (ushort) Internal();
    }
    public ushort NextUInt16(ushort maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (ushort) (Sample() * maxValue);
    }
    public ushort NextUInt16(ushort minValue, ushort maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 16)
            BitWidth = 16;
        return (ushort) Next(minValue, maxValue);
    }
    public int NextInt24()
    {
        BitWidth = 24;
        if (Unsigned)
            Unsigned = false;
        return (int) Internal();
    }
    public int NextInt24(int maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 24;
        return (int) (Sample() * maxValue);
    }
    public int NextInt24(int minValue, int maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 24;
        return (int) Next(minValue, maxValue);
    }
    public uint NextUInt24()
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 24;
        return (uint) Internal();
    }
    public uint NextUInt24(uint maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 24;
        return (uint) (Sample() * maxValue);
    }
    public uint NextUInt24(uint minValue, uint maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 24;
        return (uint) Next(minValue, maxValue);
    }
    public uint NextUInt32()
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (uint) Internal();
    }
    public uint NextUInt32(uint maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (uint) (Sample() * maxValue);
    }
    public uint NextUInt32(uint minValue, uint maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (uint) Next(minValue, maxValue);
    }
    public int NextInt32()
    {
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (int) Internal();
    }
    public int NextInt32(int maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (int) (Sample() * maxValue);
    }
    public int NextInt32(int minValue, int maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 32)
            BitWidth = 32;
        return (int) Next(minValue, maxValue);
    }
    public long NextUInt40()
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 40;
        return (long) Internal();
    }
    public long NextUInt40(long maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 40;
        return (long) (Sample() * maxValue);
    }
    public long NextUInt40(long minValue, long maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 40;
        return (long) Next(minValue, maxValue);
    }
    public long NextInt40()
    {
        if (Unsigned)
            Unsigned = false;
        BitWidth = 40;
        return (long) Internal();
    }
    public long NextInt40(long maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 40;
        return (long) (Sample() * maxValue);
    }
    public long NextInt40(long minValue, long maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 40;
        return (long) Next(minValue, maxValue);
    }
    public ulong NextUInt48()
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 48;
        return (ulong) Internal();
    }
    public ulong NextUInt48(ulong maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 48;
        return (ulong) (Sample() * maxValue);
    }
    public ulong NextUInt48(ulong minValue, ulong maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 48;
        return (ulong) Next(minValue, maxValue);
    }
    public long NextInt48()
    {
        if (Unsigned)
            Unsigned = false;
        BitWidth = 48;
        return (long) Internal();
    }
    public long NextInt48(long maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 48;
        return (long) (Sample() * maxValue);
    }
    public ulong NextUInt56()
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 56;
        return (ulong) Internal();
    }
    public ulong NextUInt56(ulong maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 56;
        return (ulong) (Sample() * maxValue);
    }
    public ulong NextUInt56(ulong minValue, ulong maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        BitWidth = 56;
        return (uint) Next(minValue, maxValue);
    }
    public long NextInt56()
    {
        if (Unsigned)
            Unsigned = false;
        BitWidth = 56;
        return (long) Internal();
    }
    public long NextInt56(long maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 56;
        return (long) (Sample() * maxValue);
    }
    public long NextInt56(long minValue, long maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        BitWidth = 48;
        return (long) Next(minValue, maxValue);
    }
    public long NextInt64()
    {
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (long) Internal();
    }
    public long NextInt64(long maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (long) (Sample() * maxValue);
    }
    public long NextInt64(long minValue, long maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (Unsigned)
            Unsigned = false;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (long) Next(minValue, maxValue);
    }
    public ulong NextUInt64()
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (ulong) Internal();
    }
    public ulong NextUInt64(ulong maxValue)
    {
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (ulong) (Sample() * maxValue);
    }
    public ulong NextUInt64(ulong minValue, ulong maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        if (!Unsigned)
            Unsigned = true;
        if (_bitWidth < 64)
            BitWidth = 64;
        return (ulong) Next(minValue, maxValue);
    }
    public BigInteger Next()
    {
        return Internal();
    }
    public BigInteger Next(BigInteger minValue, BigInteger maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        return (BigInteger) (Sample() * (maxValue - minValue)) + minValue;
    }
    public UInt512 Next(UInt512 minValue, UInt512 maxValue)
    {
        if (minValue > maxValue)
            throw new ArgumentException("maxValue must be greater than or equal to minValue");
        var s = Sample();
        var f = (BigDecimal) ((BigInteger) maxValue - (BigInteger) minValue);
        return (BigInteger) (s * f);
    }
    public BigInteger Next(BigInteger maxValue)
    {
        if (maxValue < 0)
            throw new ArgumentException("maxValue must be greater than zero.");
        return (BigInteger) (Sample() * maxValue);
    }
    public unsafe double NextDouble()
    {
        var buf = new byte[8];
        GetBytes(buf);
        fixed (byte* ptr = buf)
        {
            return *(ulong*) ptr * (1.0 / ulong.MaxValue) * ulong.MaxValue;
        }
    }
    public BigRational NextBigRational()
    {
        return new BigRational(Internal(), Internal());
    }
    public decimal NextDecimal()
    {
        return new decimal(NextInt32(), NextInt32(), NextInt32(), NextBool(), NextUInt8(255));
    }
    public BigDecimal NextBigDecimal()
    {
        return Sample();
    }
    public byte[] GetNextByteArray(int size)
    {
        var ba = new byte[size];
        _crng.GetBytes(ba);
        return ba;
    }
    public char[] GetNextCharArray(int size)
    {
        var xbc = new byte[1];
        var ca  = new char[size];
        var ptr = 0;
        do
        {
            _crng.GetBytes(xbc);
            var c = xbc[0];
            if (c >= 0x20 && c <= 0x7F)
                ca[ptr++] = (char) c;
        } while (ptr < size);
        return ca;
    }
    public char NextChar()
    {
        var xbc = new byte[1];
        while (true)
        {
            _crng.GetBytes(xbc);
            var c = xbc[0];
            if (c >= 0x20 && c <= 0x7F)
                return (char) c;
        }
    }
    public string GetRandomString(int minLen, int maxLen)
    {
        return minLen == maxLen ? new string(GetNextCharArray(minLen)) : new string(GetNextCharArray((int) NextUInt32((uint) minLen, (uint) maxLen)));
    }
    private BigDecimal Sample()
    {
        var i = Internal();
        var s = i * (BigDecimal.One / MaxValue);
        if (s.Sign == -1)
            s = s * -1;
        if (s.IsZero)
            throw new Exception("Sample is zero.");
        return s;
    }
    public void GetBytes(byte[] data)
    {
        if (data == null)
            throw new ArgumentException("The buffer cannot be null.");
        _crng.GetBytes(data);
    }
    private BigInteger Internal()
    {
        if (Unsigned)
        {
            var buffer = new byte[_maxByteWidth + 1];
            _crng.GetBytes(buffer);
            buffer[_maxByteWidth] = 0;
            var n = new BigInteger(buffer);
            return !OddsOnly ? n : n | 1;
        }
        else
        {
            var buffer = new byte[_maxByteWidth];
            _crng.GetBytes(buffer);
            var n = new BigInteger(buffer);
            return !OddsOnly ? n : n | 1;
        }
    }
}

FNV1a128b.cs

FNV1a Patterned 128Bit Hashing Algorithm using BigInteger

using System.Numerics;
using System.Security.Cryptography;
public class FNV1a128b : HashAlgorithm
{
    private readonly BigInteger K = "309485009821345068724781371".BigIntegerBase10();
    private readonly BigInteger M = "340282366920938463463374607431768211456".BigIntegerBase10();
    private          BigInteger _hash;
    private readonly BigInteger Seed = "144066263297769815596495629667062367629".BigIntegerBase10();
    public FNV1a128b()
    {
        _hash = Seed;
    }
    public FNV1a128b(BigInteger seed)
    {
        Seed  = seed;
        _hash = Seed;
    }
    public override int HashSize => 128;
    public override void Initialize()
    {
        _hash = Seed;
    }
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        Hash128(bytes, ibStart, cbSize);
    }
    protected override byte[] HashFinal()
    {
        return _hash.ToByteArray();
    }
    private unsafe void Hash128(byte[] bytes, int ibStart, int cbSize)
    {
        fixed (byte* pb = bytes)
        {
            var np = pb + ibStart;
            for (; cbSize > 0; --cbSize, np++)
                _hash = ((_hash ^ *np) * K) % M;
        }
    }
}

BigIntegerHelper.cs

BigInteger Helper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
public static class BigIntegerHelper
{
    private static readonly BigInteger Ten   = new BigInteger(10);
    private static readonly BigInteger Three = new BigInteger(3);
    private static readonly BigInteger ONE   = new BigInteger(1);
    private static readonly BigInteger Two   = new BigInteger(2);
    public static BigInteger BigIntegerBase2(this string binaryValue)
    {
        BigInteger res = 0;
        if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length) return res;
        foreach (var c in binaryValue)
        {
            res <<= 1;
            res +=  c == '1' ? 1 : 0;
        }
        return res;
    }
    public static BigInteger BigIntegerBase16(this string hexNumber)
    {
        if (string.IsNullOrEmpty(hexNumber))
            throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
        if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
            throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
        hexNumber = hexNumber.ToUpper();
        if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
            hexNumber = hexNumber.Substring(2);
        var bytes = Enumerable.Range(0, hexNumber.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
            .ToArray();
        return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
    }
    public static BigInteger BigIntegerBase10(this string str)
    {
        var number = new BigInteger();
        int i;
        for (i = 0; i < str.Length; i++)
            if (str[i] >= '0' && str[i] <= '9')
                number = number * Ten + long.Parse(str[i].ToString());
        return number;
    }
    private static byte[] HexToByteArray(this string hex)
    {
        return Enumerable.Range(0, hex.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
            .ToArray();
    }
    public static BigInteger ToBigInteger(this char ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this byte ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this sbyte ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this short ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this ushort ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this int ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this uint ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this long ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this ulong ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ModPow(this BigInteger n, BigInteger e, BigInteger m)
    {
        var s = ONE;
        var u = e;
        while (!u.IsZero)
        {
            if ((u & ONE) == 1)
                s = s * n % m;
            u >>= 1;
            n =   n * n % m;
        }
        return s;
    }
    public static double ConfidenceToProbability(int confidence)
    {
        var a = Two.Pow(confidence);
        var b = 100.0 / (double) a;
        var c = 100.0 - b;
        return c;
    }
    public static BigInteger Pow(this BigInteger n, BigInteger exp)
    {
        var y = ONE;
        var z = n;
        while (exp != 0)
        {
            if ((exp & 0x1) == 1)
                y *= z;
            exp >>= 1;
            if (exp != 0)
                z *= z;
        }
        return y;
    }
    public static List<BigInteger> GetFactors(this BigInteger n)
    {
        var Factors = new List<BigInteger>();
        var s       = n.Sqrt();
        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 BigInteger Gcd(this BigInteger a, BigInteger b)
    {
        while (b > BigInteger.Zero)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static BigInteger Lcm(this BigInteger a, BigInteger b)
    {
        return a * b / a.Gcd(b);
    }
    public static BigInteger BigIntegerBase2(this BigInteger bi, string binaryValue)
    {
        bi = BigInteger.Zero;
        if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length)
            return bi;
        foreach (var c in binaryValue)
        {
            bi <<= 1;
            bi +=  c == '1' ? 1 : 0;
        }
        return bi;
    }
    public static int GetByteWidth(this BigInteger n)
    {
        return GetBitWidth(n) >> 3;
    }
    public static int GetBitWidth(this BigInteger n)
    {
        BigInteger bitWidth = 1;
        var        v        = n;
        while ((v >>= 1) > 0)
            bitWidth++;
        if (bitWidth < 8)
            bitWidth = 8;
        return (int) bitWidth;
    }
    public static BigInteger GetMaxValue(this BigInteger bi, int bitLength)
    {
        if (bi.Sign == -1)
            bitLength -= 1;
        return BigInteger.One << bitLength;
    }
    public static BigInteger GetMaxValue(this BigInteger bi)
    {
        var bitLength = bi.GetBitWidth();
        if (bi.Sign == -1)
            bitLength -= 1;
        return (BigInteger.One << bitLength) - BigInteger.One;
    }
    public static BigInteger GetMaxValueBitWidth(int bitLength)
    {
        return (BigInteger.One << bitLength) - BigInteger.One;
    }
    public static BigInteger BigIntegerBase16(this BigInteger bi, string hexNumber)
    {
        if (string.IsNullOrEmpty(hexNumber))
            throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
        if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
            throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
        hexNumber = hexNumber.ToUpper();
        if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
            hexNumber = hexNumber.Substring(2);
        var bytes = Enumerable.Range(0, hexNumber.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
            .ToArray();
        return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
    }
    public static BigInteger BigIntegerBase10(this BigInteger bi, string str)
    {
        if (str[0] == '-' || str.IndexOf('.') != -1)
            throw new Exception($"Invalid numeric string. Only positive numbers and whole numbers are allowed. Value={str}");
        var number = new BigInteger();
        int i;
        for (i = 0; i < str.Length; i++)
            if (str[i] >= '0' && str[i] <= '9')
                number = number * Ten + long.Parse(str[i].ToString());
        return number;
    }
    public static string ToBinaryString(this BigInteger bigint)
    {
        var bytes  = bigint.ToByteArray();
        var index  = bytes.Length - 1;
        var base2  = new StringBuilder(bytes.Length * 8);
        var binary = Convert.ToString(bytes[index], 2);
        if (binary[0] != '0' && bigint.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 static string ToHexString(this BigInteger bi)
    {
        var bytes = bi.ToByteArray();
        var sb    = new StringBuilder();
        foreach (var b in bytes)
        {
            var hex = b.ToString("X2");
            sb.Append(hex);
        }
        return sb.ToString();
    }
    public static string ToOctalString(this BigInteger bigint)
    {
        var bytes         = bigint.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' && bigint.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();
    }
    /// <summary>
    ///     Approximation
    /// </summary>
    public static BigInteger Sqrt(this BigInteger n)
    {
        var q = BigInteger.One << ((int) BigInteger.Log(n, 2) >> 1);
        var m = BigInteger.Zero;
        while (BigInteger.Abs(q - m) >= 1)
        {
            m = q;
            q = (m + n / m) >> 1;
        }
        return q;
    }
}

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

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

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

BigIntegerPrime.cs

Generate or Test BigInteger Primes

using System;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
public class BigIntegerPrime
{
    private readonly int _bitWidth;
    private readonly int[] _lowPrimes =
    {
        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
        157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
        317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
        499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
        683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881,
        883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
    };
    private readonly BigIntegerRng _rng;
    private readonly BigInteger    _twosixtyfour = "18446744073709551616".BigIntegerBase10();
    private readonly uint[]        _w0           = {2};
    private readonly uint[]        _w1           = {2, 3};
    private readonly uint[]        _w10          = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    private readonly uint[]        _w11          = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    private readonly uint[]        _w12          = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
    private readonly uint[]        _w2           = {31, 73};
    private readonly uint[]        _w3           = {2, 3, 5};
    private readonly uint[]        _w4           = {2, 3, 5, 7};
    private readonly uint[]        _w5           = {2, 7, 61};
    private readonly uint[]        _w6           = {2, 13, 23, 1662803};
    private readonly uint[]        _w7           = {2, 3, 5, 7, 11};
    private readonly uint[]        _w8           = {2, 3, 5, 7, 11, 13};
    private readonly uint[]        _w9           = {2, 3, 5, 7, 11, 13, 17};
    private          BigInteger    _nextPrime;
    public BigIntegerPrime(int BitWidthHint)
    {
        _rng          = new BigIntegerRng(BitWidthHint);
        _rng.Unsigned = true;
        _rng.OddsOnly = true;
        _bitWidth     = BitWidthHint;
    }
    public bool IsPrime(BigInteger candidate)
    {
        if (candidate == 1)
            return false;
        if (candidate == 2 || candidate == 3 || candidate == 5)
            return true;
        if (candidate <= 1000)
            return CheckLowPrimes(candidate);
        if (!PrimeCheckM10LD(candidate))
            return false;
        if (!PrimeCheckM6(candidate))
            return false;
        if (!TrialDivision(candidate))
            return false;
        if (candidate < 2047)
        {
            if (!MillerRabin(candidate, _w0))
                return false;
        }
        else if (candidate > 2047 && candidate <= 1373653)
        {
            if (!MillerRabin(candidate, _w1))
                return false;
        }
        else if (candidate > 1373653 && candidate <= 9080191)
        {
            if (!MillerRabin(candidate, _w2))
                return false;
        }
        else if (candidate > 9080191 && candidate <= 25326001)
        {
            if (!MillerRabin(candidate, _w3))
                return false;
        }
        else if (candidate > 25326001 && candidate <= 3215031751)
        {
            if (!MillerRabin(candidate, _w4))
                return false;
        }
        else if (candidate > 3215031751 && candidate <= 4759123141)
        {
            if (!MillerRabin(candidate, _w5))
                return false;
        }
        else if (candidate > 4759123141 && candidate <= 1122004669633)
        {
            if (!MillerRabin(candidate, _w6))
                return false;
        }
        else if (candidate > 1122004669633 && candidate <= 2152302898747)
        {
            if (!MillerRabin(candidate, _w7))
                return false;
        }
        else if (candidate > 2152302898747 && candidate <= 3474749660383)
        {
            if (!MillerRabin(candidate, _w8))
                return false;
        }
        else if (candidate > 3474749660383 && candidate <= 341550071728321)
        {
            if (!MillerRabin(candidate, _w9))
                return false;
        }
        else if (candidate > 341550071728321 && candidate <= 3825123056546413051)
        {
            if (!MillerRabin(candidate, _w10))
                return false;
        }
        else if (candidate > 3825123056546413051 && candidate < _twosixtyfour)
        {
            if (!MillerRabin(candidate, _w11))
                return false;
        }
        else if (candidate > _twosixtyfour)
        {
            if (!MillerRabin(candidate, _w12))
                return false;
        }
        return true;
    }
    private bool MillerRabin(BigInteger candidate, uint[] w)
    {
        var s = 0;
        var d = candidate - BigInteger.One;
        while ((d & 1) == 0)
        {
            d >>= 1;
            s++;
        }
        if (s == 0)
            return false;
        var nmo = candidate - BigInteger.One;
        for (var i = 0; i < w.Length; ++i)
        {
            BigInteger a;
            if (candidate > _twosixtyfour)
                a = _rng.Next(3, nmo);
            else
                a = w[i];
            var x = BigInteger.ModPow(a, d, candidate);
            if (x == 1 || x == nmo)
                continue;
            for (var r = 1; r < s; ++r)
            {
                x = BigInteger.ModPow(x, 2, candidate);
                if (x == 1)
                    return false;
                if (x == nmo)
                    break;
            }
            if (x == nmo)
                continue;
            return false;
        }
        return true;
    }
    private bool TrialDivision(BigInteger candidate)
    {
        for (var i = 0; i < _lowPrimes.Length; i++)
        {
            var p = _lowPrimes[i];
            if (i < p)
            {
                if (candidate % p != 0)
                    continue;
                return false;
            }
            break;
        }
        return true;
    }
    private static bool PrimeCheckM10LD(BigInteger n)
    {
        var d1 = (int) (n % 10);
        return d1 == 1 || d1 == 3 || d1 == 7 || d1 == 9;
    }
    private static bool PrimeCheckM6(BigInteger n)
    {
        var d1 = (int) (n % 6);
        return d1 == 1 || d1 == 5;
    }
    private bool CheckLowPrimes(BigInteger val)
    {
        foreach (var v in _lowPrimes)
            if (val == v)
                return true;
        return false;
    }
    private void NextPrime(int t)
    {
        if (t > Environment.ProcessorCount - 1)
            return;
        var MaxValue = (BigInteger.One << _bitWidth) - 1;
        while (true)
        {
            if (_nextPrime != 0)
                return;
            var n = _rng.NextFast(_bitWidth);
            if (_nextPrime != 0)
                return;
            if (n == 1)
                continue;
            if (n > MaxValue)
                continue;
            if (!IsPrime(n))
                continue;
            _nextPrime = n;
        }
    }
    public BigInteger GetPrime()
    {
        void invoke()
        {
            Parallel.Invoke(new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount},
                () => NextPrime(1),
                () => NextPrime(2),
                () => NextPrime(3),
                () => NextPrime(4),
                () => NextPrime(5),
                () => NextPrime(6),
                () => NextPrime(7),
                () => NextPrime(8),
                () => NextPrime(9),
                () => NextPrime(10),
                () => NextPrime(11),
                () => NextPrime(12),
                () => NextPrime(13),
                () => NextPrime(14),
                () => NextPrime(15),
                () => NextPrime(16)
            );
        }
        _nextPrime = 0;
        var thread = new Thread(invoke) {Priority = ThreadPriority.Highest};
        thread.Start();
        thread.Join();
        return _nextPrime;
    }
}