Featured

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

DigitsArray.cs

Support File, for use with BigIntX.cs

using System;
using System.Collections.Generic;
using System.Text;
public class DigitsArray
{
    public static readonly uint   AllBits;
    public static readonly uint   HiBitSet;
    public                 uint[] Data;
    static DigitsArray()
    {
        unchecked
        {
            AllBits  = ~(uint) 0;
            HiBitSet = (uint) 1 << (DataSizeBits - 1);
        }
    }
    public DigitsArray(int size)
    {
        Allocate(size, 0);
    }
    public DigitsArray(int size, int used)
    {
        Allocate(size, used);
    }
    public DigitsArray(uint[] copyFrom)
    {
        Allocate(copyFrom.Length);
        CopyFrom(copyFrom, 0, 0, copyFrom.Length);
        ResetDataUsed();
    }
    public DigitsArray(DigitsArray copyFrom)
    {
        Allocate(copyFrom.Count - 1, copyFrom.DataUsed);
        Array.Copy(copyFrom.Data, 0, Data, 0, copyFrom.Count);
    }
    public static int DataSizeOf   => sizeof(uint);
    public static int DataSizeBits => sizeof(uint) * 8;
    public uint this[int index]
    {
        get
        {
            if (index < Data.Length)
                return Data[index];
            return IsNegative ? AllBits : 0;
        }
        set => Data[index] = value;
    }
    public int DataUsed
    {
        get;
        set;
    }
    public int  Count      => Data.Length;
    public bool IsZero     => DataUsed == 0 || DataUsed == 1 && Data[0] == 0;
    public bool IsNegative => (Data[Data.Length - 1] & HiBitSet) == HiBitSet;
    public int Sign
    {
        get
        {
            if (Data[0] == 0)
                return 0;
            if (IsNegative)
                return -1;
            return 1;
        }
    }
    public void Allocate(int size)
    {
        Allocate(size, 0);
    }
    public void Allocate(int size, int used)
    {
        Data     = new uint[size + 1];
        DataUsed = used;
    }
    public void CopyFrom(uint[] source, int sourceOffset, int offset, int length)
    {
        Array.Copy(source, sourceOffset, Data, 0, length);
    }
    public void CopyTo(uint[] array, int offset, int length)
    {
        Array.Copy(Data, 0, array, offset, length);
    }
    public string GetDataAsString()
    {
        var result = new StringBuilder();
        foreach (var data in Data)
            result.Append(data + " ");
        return result.ToString();
    }
    public byte[] ToByteArray()
    {
        if (Data == null && Sign == 0)
            return new byte[1];
        uint[] dwords;
        byte   highByte;
        if (Data == null)
        {
            dwords   = new uint[1] {(uint) Sign};
            highByte = Sign < 0 ? byte.MaxValue : (byte) 0;
        }
        else if (Sign == -1)
        {
            dwords   = (uint[]) Data.Clone();
            dwords   = TwosComplement(dwords);
            highByte = byte.MaxValue;
        }
        else
        {
            dwords   = Data;
            highByte = 0;
        }
        var bytes   = new byte[checked(4 * dwords.Length)];
        var curByte = 0;
        for (var i = 0; i < dwords.Length; ++i)
        {
            var dword = dwords[i];
            for (var j = 0; j < 4; ++j)
            {
                bytes[curByte++] =   (byte) (dword & byte.MaxValue);
                dword            >>= 8;
            }
        }
        var msb = bytes.Length - 1;
        while (msb > 0 && bytes[msb] == highByte)
            --msb;
        var needExtraByte = (bytes[msb] & 128) != (highByte & 128);
        var timmedBytes   = new byte[msb + 1 + (needExtraByte ? 1 : 0)];
        Array.Copy(bytes, timmedBytes, msb + 1);
        if (needExtraByte)
            timmedBytes[timmedBytes.Length - 1] = highByte;
        return timmedBytes;
    }
    public uint[] ToUIn32Array()
    {
        var value = ToByteArray();
        var al    = value.Length >> 2;
        if (al << 2 != value.Length)
            al++;
        var arr = new uint[al];
        Buffer.BlockCopy(value, 0, arr, 0, value.Length);
        return arr;
    }
    public ulong[] ToUIn64Array()
    {
        var value = ToByteArray();
        var al    = value.Length >> 3;
        if (al << 3 != value.Length)
            al++;
        var arr = new ulong[al];
        Buffer.BlockCopy(value, 0, arr, 0, value.Length);
        return arr;
    }
    private static 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 void ResetDataUsed()
    {
        DataUsed = Data.Length;
        if (IsNegative)
        {
            while (DataUsed > 1 && Data[DataUsed - 1] == AllBits)
                --DataUsed;
            DataUsed++;
        }
        else
        {
            while (DataUsed > 1 && Data[DataUsed - 1] == 0)
                --DataUsed;
            if (DataUsed == 0)
                DataUsed = 1;
        }
    }
    public int ShiftRight(int shiftCount)
    {
        return ShiftRight(Data, shiftCount);
    }
    public static int ShiftRight(uint[] buffer, int shiftCount)
    {
        var shiftAmount = DataSizeBits;
        var invShift    = 0;
        var bufLen      = buffer.Length;
        while (bufLen > 1 && buffer[bufLen - 1] == 0)
            bufLen--;
        for (var count = shiftCount; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
            {
                shiftAmount = count;
                invShift    = DataSizeBits - shiftAmount;
            }
            ulong carry = 0;
            for (var i = bufLen - 1; i >= 0; i--)
            {
                var val = (ulong) buffer[i] >> shiftAmount;
                val       |= carry;
                carry     =  (ulong) buffer[i] << invShift;
                buffer[i] =  (uint) val;
            }
        }
        while (bufLen > 1 && buffer[bufLen - 1] == 0)
            bufLen--;
        return bufLen;
    }
    public int ShiftLeft(int shiftCount)
    {
        return ShiftLeft(Data, shiftCount);
    }
    public static int ShiftLeft(uint[] buffer, int shiftCount)
    {
        var shiftAmount = DataSizeBits;
        var bufLen      = buffer.Length;
        while (bufLen > 1 && buffer[bufLen - 1] == 0)
            bufLen--;
        for (var count = shiftCount; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
                shiftAmount = count;
            ulong carry = 0;
            for (var i = 0; i < bufLen; i++)
            {
                var val = (ulong) buffer[i] << shiftAmount;
                val       |= carry;
                buffer[i] =  (uint) (val & AllBits);
                carry     =  val >> DataSizeBits;
            }
            if (carry != 0)
            {
                if (bufLen + 1 <= buffer.Length)
                {
                    buffer[bufLen] = (uint) carry;
                    bufLen++;
                    carry = 0;
                }
                else
                {
                    throw new OverflowException();
                }
            }
        }
        return bufLen;
    }
    public int ShiftLeftWithoutOverflow(int shiftCount)
    {
        if (shiftCount == 0) return Data.Length;
        var temporary   = new List<uint>(Data);
        var shiftAmount = DataSizeBits;
        for (var count = shiftCount; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
                shiftAmount = count;
            ulong carry = 0;
            for (var i = 0; i < temporary.Count; i++)
            {
                var val = (ulong) temporary[i] << shiftAmount;
                val          |= carry;
                temporary[i] =  (uint) (val & AllBits);
                carry        =  val >> DataSizeBits;
            }
            if (carry != 0)
            {
                var lastNum = (uint) carry;
                if (IsNegative)
                {
                    var byteCount = (int) Math.Floor(Math.Log(carry, 2));
                    lastNum = (0xffffffff << byteCount) | (uint) carry;
                }
                temporary.Add(lastNum);
            }
        }
        Data = new uint[temporary.Count];
        temporary.CopyTo(Data);
        return Data.Length;
    }
}

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

Fnv1a64Fast.cs

FNV1a 64Bit Hashing Fast

using System.Security.Cryptography;
/// <summary>
///     https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
///     Very fast up to 2GBpS.
/// </summary>
public class Fnv1a64Fast : HashAlgorithm
{
    private const    ulong _prime  = 0x100000001B3;
    private readonly ulong _offset = 0xCBF29CE484222325;
    private          ulong _hash;
    public           ulong Hashf;
    public Fnv1a64Fast()
    {
        _hash = _offset;
    }
    public Fnv1a64Fast(ulong seed)
    {
        _offset = seed;
        _hash   = _offset;
    }
    public override int HashSize => 64;
    public override void Initialize()
    {
        _hash = _offset;
    }
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        Hash64(bytes, ibStart, cbSize);
    }
    private unsafe void Hash64(byte[] bytes, int ibStart, int cbSize)
    {
        if (bytes == null)
            return;
        if (ibStart >= bytes.Length || cbSize > bytes.Length)
            return;
        unchecked
        {
            fixed (byte* pb = bytes)
            {
                var nb = pb + ibStart;
                while (cbSize >= 8)
                {
                    _hash  ^= *(ulong*) nb;
                    _hash  *= _prime;
                    nb     += 8;
                    cbSize -= 8;
                }
                switch (cbSize & 7)
                {
                    case 7:
                        _hash ^= *(ulong*) (nb + 6);
                        _hash *= _prime;
                        goto case 6;
                    case 6:
                        _hash ^= *(ulong*) (nb + 5);
                        _hash *= _prime;
                        goto case 5;
                    case 5:
                        _hash ^= *(ulong*) (nb + 4);
                        _hash *= _prime;
                        goto case 4;
                    case 4:
                        _hash ^= *(ulong*) (nb + 3);
                        _hash *= _prime;
                        goto case 3;
                    case 3:
                        _hash ^= *(ulong*) (nb + 2);
                        _hash *= _prime;
                        goto case 2;
                    case 2:
                        _hash ^= *(ulong*) (nb + 1);
                        _hash *= _prime;
                        goto case 1;
                    case 1:
                        _hash ^= *nb;
                        _hash *= _prime;
                        break;
                }
            }
        }
        _hash = _hash ^ (_hash >> 32);
        Hashf = _hash;
    }
    protected override byte[] HashFinal()
    {
        var ba = new byte[8];
        unsafe
        {
            fixed (byte* ptr = ba)
            {
                *(ulong*) ptr = _hash;
            }
        }
        return ba;
    }
}

FNV1a32Fast.cs

FNV1a 32 Bit Fast Hashing

using System.Security.Cryptography;
/// <summary>
///     https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
///     Very fast 2GBpS.
/// </summary>
public class FNV1a32Fast : HashAlgorithm
{
    private const    ulong _prime = 0x100000001B3;
    private          ulong _hash;
    private readonly ulong _offset = 0xCBF29CE484222325;
    public           uint  Hashf;
    public FNV1a32Fast()
    {
        _hash = _offset;
    }
    public FNV1a32Fast(ulong seed)
    {
        _offset = seed;
        _hash   = _offset;
    }
    public override int HashSize => 32;
    public override void Initialize()
    {
        _hash = _offset;
    }
    protected override void HashCore(byte[] bytes, int ibStart, int cbSize)
    {
        Hash64(bytes, ibStart, cbSize);
    }
    private unsafe void Hash64(byte[] bytes, int ibStart, int cbSize)
    {
        if (bytes == null)
            return;
        if (ibStart >= bytes.Length || cbSize > bytes.Length)
            return;
        unchecked
        {
            fixed (byte* pb = bytes)
            {
                var nb = pb + ibStart;
                while (cbSize >= 8)
                {
                    _hash  ^= *(ulong*) nb;
                    _hash  *= _prime;
                    nb     += 8;
                    cbSize -= 8;
                }
                switch (cbSize & 7)
                {
                    case 7:
                        _hash ^= *(ulong*) (nb + 6);
                        _hash *= _prime;
                        goto case 6;
                    case 6:
                        _hash ^= *(ulong*) (nb + 5);
                        _hash *= _prime;
                        goto case 5;
                    case 5:
                        _hash ^= *(ulong*) (nb + 4);
                        _hash *= _prime;
                        goto case 4;
                    case 4:
                        _hash ^= *(ulong*) (nb + 3);
                        _hash *= _prime;
                        goto case 3;
                    case 3:
                        _hash ^= *(ulong*) (nb + 2);
                        _hash *= _prime;
                        goto case 2;
                    case 2:
                        _hash ^= *(ulong*) (nb + 1);
                        _hash *= _prime;
                        goto case 1;
                    case 1:
                        _hash ^= *nb;
                        _hash *= _prime;
                        break;
                }
            }
        }
        var usb = (uint) (_hash >> 32);
        var lsb = (uint) _hash;
        Hashf = usb ^ lsb;
    }
    protected override byte[] HashFinal()
    {
        var ba = new byte[4];
        unsafe
        {
            fixed (byte* ptr = ba)
            {
                *(uint*) ptr = Hashf;
            }
        }
        return ba;
    }
}

RandomBigInt.cs

BigInteger Random Number Generator

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);
        _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 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 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(Internal(), Internal());
    }
    public decimal NextDecimal()
    {
        return new((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(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.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;
        }
    }
    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();
    }
}

ObjectAssociativeArray.cs

Key based, Fast Searchable Values, Multiple Value Arrays.

Key, Values, Values,… All Comparisons are done at the byte level, no need for custom comparator. Keys and all values are searchable, using (Indexed) variable bit width map hashing (Default is 64 Bits, can go up to 512 bits).

using System;
using System.Diagnostics;
[DebuggerDisplay("Count = {" + nameof(Count) + "}")]
[Serializable]
public class ObjectAssociativeArray : MonitorActionFuncWrapper
{
    private ObjectBase   _keys;
    private ObjectBase[] _values;
    public ObjectAssociativeArray() : this(1024)
    {
    }
    public ObjectAssociativeArray(int size, int order = 1)
    {
        _keys   = new ObjectBase(size);
        _values = new ObjectBase[order];
        for (var i = 0; i < order; ++i)
            _values[i] = new ObjectBase(size);
    }
    public int Count => _keys.Count;
    public bool Add(object key, params object[] value)
    {
        if (key == null)
            throw new ArgumentException("Key cannot be null.");
        if (value == null)
            throw new ArgumentException("There must be a least one value.");
        return Lock(this, () =>
        {
            for (var p = 0; p < value.Length; ++p)
                if (_keys.Contains(key) || _values[p].Contains(value[p]))
                    return false;
            var ka = _keys.Add(key);
            var va = false;
            for (var p = 0; p < value.Length; ++p)
            {
                va = _values[p].Add(value[p]);
                if (!va)
                    throw new Exception($"the nth parameter {p} could not be added.");
            }
            return ka && va;
        });
    }
    public (object[] key, object value) GetKeysAndValues(int index)
    {
        return (_keys.ToArray(), _values[index].ToArray());
    }
    #region Generic
    public bool Contains(object key)
    {
        return _keys.Contains(key);
    }
    public bool ContainsValue(object value, int index = 0)
    {
        return _values[index].Contains(value);
    }
    #endregion
}

ObjectDictionary.cs

Object Dictionary Single Associative Array, Searchable Keys and Values

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class ObjectDictionary : MonitorActionFuncWrapper, IEnumerable<KeyValuePair<object, object>>
{
    public ObjectBase _keys;
    public ObjectBase _values;
    public ObjectDictionary() : this(101)
    {
    }
    public ObjectDictionary(int size)
    {
        _keys   = new ObjectBase(size);
        _values = new ObjectBase(size);
    }
    public ObjectDictionary(IEnumerable<KeyValuePair<object, object>> collection)
    {
        foreach (var kp in collection)
            Add(kp.Key, kp.Value);
    }
    public int Count => _keys.Count;
    public object this[object key]
    {
        get
        {
            var pos = _keys.GetObjectIndex(key, false);
            return pos.idx == -1 ? default : _values[pos.idx];
        }
        set => Add(key, value);
    }
    public object[]                       Values        => _values.ToArray();
    public KeyValuePair<object, object>[] KeyValuePairs => ToArray();
    public object[]                       Keys          => _keys.ToArray();
    public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
    {
        return Lock(this, () =>
        {
            return GetEnum();
        });
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return Lock(this, () =>
        {
            return GetEnum();
        });
    }
    private IEnumerator<KeyValuePair<object, object>> GetEnum()
    {
        for (var i = 0; i < Count; i++)
            yield return new KeyValuePair<object, object>(_keys[i], _values[i]);
    }
    public bool Add(object key, object value)
    {
        return Lock(this, () =>
        {
            if (_keys.Contains(key) || _values.Contains(value))
                return false;
            
            var ka = _keys.Add(key);
            var va = _values.Add(value);

            if (ka || va)
            {
                int a = 1;
            }

            return ka && va;
        });
    }
    public void RemoveKey(object key)
    {
        var kidx = _keys.GetObjectIndex(key, false).idx;
        if (kidx != -1)
        {
            var keys   = _keys;
            var values = _values;
            Clear();
            for (var i = 0; i < keys.Count; ++i)
                if (i != kidx)
                    Add(keys[i], values[i]);
        }
    }
    public void RemoveValue(object value)
    {
        var vidx = _values.GetObjectIndex(value, false).idx;
        if (vidx != -1)
        {
            var keys   = _keys;
            var values = _values;
            Clear();
            for (var i = 0; i < keys.Count; ++i)
                if (i != vidx)
                    Add(keys[i], values[i]);
        }
    }
    public void RebuildLists()
    {
        var keys   = _keys;
        var values = _values;
        Clear();
        for (var i = 0; i < keys.Count; ++i)
            Add(keys[i], values[i]);
    }
    public bool ContainsKey(object key)
    {
        return Lock(this, () =>
        {
            return _keys.Contains(key);
        });
    }
    public bool ContainsValue(object value)
    {
        return Lock(this, () =>
        {
            return _values.Contains(value);
        });
    }
    public int FindKeyIndex(object key)
    {
        return Lock(this, () =>
        {
            return _keys.GetObjectIndex(key, false).idx;
        });
    }
    public int FindValueIndex(object value)
    {
        return Lock(this, () =>
        {
            return _values.GetObjectIndex(value, false).idx;
        });
    }
    public KeyValuePair<object, object>[] ToArray()
    {
        return Lock(this, () =>
        {
            var array = new KeyValuePair<object, object>[Count];
            for (var i = 0; i < Count; i++)
                array[i] = new KeyValuePair<object, object>(_keys[i], _values[i]);
            return array;
        });
    }
    public void Clear()
    {
        Lock(this, () =>
        {
            _keys   = new ObjectBase(_keys.Count);
            _values = new ObjectBase(_values.Count);
        });
    }
}

SizeHelper32.cs

Gradually Resize

public class SizeHelper32
{
    private static readonly ulong[] SCurve =
    {
        1024, 1616, 2496, 3776, 5616, 8224, 11873, 16935, 23891, 33376, 46193, 63396, 86336, 116720, 156736, 209152, 277441, 365985, 480262, 627107, 815024, 1054544, 1358704,
        1743552, 2228834, 2838768, 3602962, 4557568, 5746578, 7223456, 9053008, 11313632, 14099984, 17526117, 21729223, 26874016, 33157888, 40817024, 50133527, 61443840, 75148560,
        91723952, 111735408, 135853168, 164870640, 199725776, 241525904, 291576640, 351415424, 422850471, 508005888, 609373904, 729875314, 872929328, 1042534194, 1243360240,
        1480857072, 1761377058, 2092317379, 2482283348, 4964566704, 9929133408, 19858266816, 39716533632, 79433067264, 158866134528, 317732269056, 635464538112, 1270929076224,
        2541858152448, 5083716304896, 10167432609792, 20334865219584, 40669730439168, 81339460878336, 162678921756672, 325357843513344, 650715687026688, 1301431374053376,
        2602862748106752, 5205725496213504, 10411450992427008, 20822901984854016, 41645803969708032, 83291607939416064, 166583215878832128, 333166431757664256, 666332863515328512,
        1332665727030657024, 2665331454061314048, 5330662908122628096, 10661325816245256192, 2875907558780960768, 5751815117561921536, 11503630235123843072, 4560516396538134528,
        9121032793076269056, 18242065586152538112, 18037387098595524608, 17628030123481497600, 16809316173253443584, 15171888272797335552, 11897032471885119488,
        5347320870060687360, 10694641740121374720, 2942539406533197824, 5885078813066395648, 11770157626132791296, 5093571178556030976, 10187142357112061952, 1927540640514572288,
        3855081281029144576, 7710162562058289152, 15420325124116578304, 12393906174523604992, 6341068275337658368, 12682136550675316736, 6917529027641081856, 13835058055282163712,
        9223372036854775808, 18446744073709551615
    };
    private readonly ulong limit = int.MaxValue;
    public ulong GetNewSize(ulong currentSize)
    {
        foreach (var v in SCurve)
        {
            if (v >= limit)
                return limit;
            if (v > currentSize)
                return v;
        }
        return limit;
    }
}

ObjectBase.cs

Base Object Array Class

Updated: May-13,2021

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Threading;
/// <summary>
///     Name: ObjectBase.cs
///     Date: January-1, 2021
///     Note: Map indexed object array.
///     Attn: Should be used for ALL object classes going forward *** MJS *** 4.11.21
/// </summary>
[DebuggerDisplay("Count = {" + nameof(Count) + "}")]
[Serializable]
public class ObjectBase : MonitorActionFuncWrapper, IEnumerable<object>
{
    [NonSerialized] private static readonly SizeHelper32      _sh = new();
    [NonSerialized] private readonly        SerializationInfo _sInfo;
    private volatile                        object[]          _array;
    [NonSerialized] private                 HashAlgorithm     _hash;
    [NonSerialized] private volatile        Set20B            _map;
    public volatile                         int               Count;
    public ObjectBase() : this(1024)
    {
    }
    public ObjectBase(int size, int bitWidth = 64)
    {
        BitWidth = bitWidth;
        SelectHashAlgorithm(bitWidth);
        _array = new object[size];
        _map   = new Set20B(size);
    }
    public ObjectBase(ICollection col, int size, int bitWidth = 64)
    {
        BitWidth = bitWidth;
        SelectHashAlgorithm(bitWidth);
        _array = new object[size];
        _map   = new Set20B(size);
        AddRange(col);
    }
    protected ObjectBase(SerializationInfo info, StreamingContext context)
    {
        _sInfo = info;
    }
    public object this[int index]
    {
        get
        {
            return Lock(this, () =>
            {
                var array = _array;
                if (index >= Count)
                    throw new Exception("Error: Index out of range.");
                return array[index];
            });
        }
    }
    public int BitWidth
    {
        get;
        private set;
    }
    public KeyValuePair<IGrouping<int, int>, int>[] BucketDepthList
    {
        get
        {
            var lst = new List<int>();
            foreach (var i in _map)
                lst.Add(_map.GetBucketDepth(i).bucketDepth);
            var grp = lst.GroupBy(x => x)
                .Where(g => g.Count() > 1)
                .ToDictionary(x => x, y => y.Count())
                .ToArray()
                .OrderByDescending(z => z.Value)
                .ToArray();
            return grp;
        }
    }
    public IEnumerator<object> GetEnumerator()
    {
        return Lock(this, () =>
        {
            return GetEnum();
        });
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return Lock(this, () =>
        {
            return GetEnum();
        });
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("BitWidth", BitWidth);
        info.AddValue("Array",    _array, typeof(object[]));
    }
    public void OnDeserialization(object sender)
    {
        if (_sInfo == null)
            return;
        BitWidth = _sInfo.GetInt32("BitWidth");
        Count    = 0;
        var array = (object[]) _sInfo.GetValue("Array", typeof(object[]));
        if (array == null)
            throw new Exception("Array cannot be null.");
        foreach (var t in array)
            Add(t);
    }
    public bool Add(object item)
    {
        return Lock(this, () =>
        {
            var (idx, exists) = GetIndex(item);
            if (idx >= _array.Length)
                Array.Resize(ref _array, (int) _sh.GetNewSize((ulong) _array.Length));
            if (exists == false)
            {
                _array[idx] = item;
                Interlocked.Increment(ref Count);
            }
            return exists == false;
        });
    }
    public void AddRange(ICollection col)
    {
        var array = col.Cast<object>().ToArray();
        array.AsParallel().WithDegreeOfParallelism(2).ForAll(i =>
        {
            Add(i);
        });
    }
    public void Clear()
    {
        Lock(this, () =>
        {
            _array = new object[_array.Length];
            _map   = new Set20B(_array.Length);
        });
    }
    private IEnumerator<object> GetEnum()
    {
        var ary = _array;
        var cnt = Count;
        for (var i = 0; i < cnt; i++)
            yield return ary[i];
    }
    public object[] ToArray()
    {
        return Lock(this, () =>
        {
            var newArray = new object[Count];
            using (var en = GetEnumerator())
            {
                var ptr = 0;
                while (en.MoveNext())
                {
                    var value = en.Current;
                    if (value == null)
                        break;
                    newArray[ptr++] = value;
                }
                return newArray;
            }
        });
    }
    public bool Contains(object item)
    {
        return Lock(this, () =>
        {
            return MapContains(item);
        });
    }
    private bool MapContains(object obj)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
        var bytes = obj.GetBytes();
        var hash  = _hash.ComputeHash(bytes, 0, bytes.Length);
        return _map.Contains(hash);
    }
    internal (int idx, bool exists) GetIndex(object obj, bool add = true)
    {
        return Lock(this, () =>
        {
            var bytes  = obj.GetBytes();
            var hash   = _hash.ComputeHash(bytes, 0, bytes.Length);
            var exists = false;
            if (!_map.Contains(hash))
            {
                if (add)
                    _map.Add(hash);
            }
            else
            {
                exists = true;
            }
            return (_map.FindEntry(hash), exists);
        });
    }
    private void SelectHashAlgorithm(int bitWidth)
    {
        BitWidth = bitWidth;
        switch (bitWidth)
        {
            case 64:
                _hash = new Fnv1a64Fast();
                break;
            case 128:
                _hash = new FNVx();
                break;
            case 256:
                _hash = new FNVx(256);
                break;
            case 512:
                _hash = new FNVx(512);
                break;
            case 1024:
                _hash = new FNVx(1024);
                break;
            default:
                throw new ArgumentException("Supported bit widths are: 64, 128, 256, 512 and 1024 Bits.");
        }
    }
    public bool Remove(object item)
    {
        var (idx, exists) = GetIndex(item);
        if (exists)
        {
            var tob = new ObjectBase(_array.Length, BitWidth);
            for (var i = 0; i < Count; i++)
                if (i != idx)
                    tob.Add(_array[i]);
            Count  = tob.Count;
            _array = tob._array;
            _map   = tob._map;
            return true;
        }
        return false;
    }
    public bool Remove(int index)
    {
        if (index < _array.Length)
        {
            var (idx, exists) = GetIndex(_array[index]);
            if (exists && idx == index)
            {
                var tob = new ObjectBase(_array.Length, BitWidth);
                for (var i = 0; i < Count; i++)
                    if (i != idx)
                        tob.Add(_array[i]);
                Count  = tob.Count;
                _array = tob._array;
                _map   = tob._map;
                return true;
            }
        }
        return false;
    }
    public void UnionWith(IEnumerable<object> other)
    {
        if (other == null)
            throw new ArgumentNullException(nameof(other));
        foreach (var obj in other)
            Add(obj);
    }
    public void ExceptWith(IEnumerable<object> other)
    {
        if (other == null)
            throw new ArgumentNullException(nameof(other));
        if (Equals(other, this))
            Clear();
        else
            foreach (var obj in other)
                Remove(obj);
    }
    public bool Overlaps(IEnumerable<object> other)
    {
        if (other == null)
            throw new ArgumentNullException(nameof(other));
        foreach (var obj in other)
            if (Contains(obj))
                return true;
        return false;
    }
    public int RemoveWhere(Predicate<object> match)
    {
        if (match == null)
            throw new ArgumentNullException(nameof(match));
        var num = 0;
        for (var i = 0; i < Count; ++i)
        {
            var obj = _array[i];
            if (match(obj) && Remove(obj))
                ++num;
        }
        return num;
    }
    public bool ContainsAllElements(IEnumerable<object> other)
    {
        foreach (var obj in other)
            if (!Contains(obj))
                return false;
        return true;
    }
    public void TrimExcess()
    {
        Array.Resize(ref _array, Count);
    }
    internal class Set20B
    {
        private int    _count;
        private int[]  _hashBuckets;
        private Slot[] _slots;
        public Set20B(int size)
        {
            _hashBuckets = new int[size];
            _slots       = new Slot[size];
            _count       = 0;
        }
        public IEnumerator<byte[]> GetEnumerator()
        {
            for (var i = 0; i < _count; i++)
                if (_slots[i].HashCode > 0)
                    yield return _slots[i].Value;
        }
        public bool Add(byte[] item)
        {
            var hashCode = GetHashCode(item) & int.MaxValue;
            if (FindEntry(item, hashCode) != -1)
                return true;
            if (_count >= _slots.Length)
                Resize();
            var hashPos = hashCode % _hashBuckets.Length;
            _slots[_count].Next     = _hashBuckets[hashPos] - 1;
            _slots[_count].Value    = item;
            _slots[_count].HashCode = hashCode;
            _hashBuckets[hashPos]   = _count + 1;
            ++_count;
            return false;
        }
        private void Resize()
        {
            var newSize        = (int) _sh.GetNewSize((ulong) _hashBuckets.Length);
            var newSlots       = new Slot[newSize];
            var newHashBuckets = new int[newSize];
            var newCount       = 0;
            var en             = GetEnumerator();
            while (en.MoveNext())
            {
                var item     = en.Current;
                var hashCode = GetHashCode(item) & int.MaxValue;
                var hashPos  = hashCode % newHashBuckets.Length;
                newSlots[newCount].Next     = newHashBuckets[hashPos] - 1;
                newSlots[newCount].Value    = item;
                newSlots[newCount].HashCode = hashCode;
                newHashBuckets[hashPos]     = newCount + 1;
                ++newCount;
            }
            _slots       = newSlots;
            _hashBuckets = newHashBuckets;
            _count       = newCount;
        }
        private int FindEntry(byte[] item, int hashCode)
        {
            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
                if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
                    return position;
            return -1;
        }
        public int FindEntry(byte[] item)
        {
            var hashCode = GetHashCode(item) & int.MaxValue;
            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
                if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
                    return position;
            return -1;
        }
        public (int bucketDepth, bool exists) GetBucketDepth(byte[] item)
        {
            var hashCode    = GetHashCode(item) & int.MaxValue;
            var bucketDepth = 1;
            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
            {
                if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
                    return (bucketDepth, true);
                ++bucketDepth;
            }
            return (-1, false);
        }
        public bool Contains(byte[] item)
        {
            return FindEntry(item, GetHashCode(item) & int.MaxValue) != -1;
        }
        private static bool Equals(byte[] x, byte[] y)
        {
            if (x == null || y == null)
                return false;
            return x.Length == y.Length && x.Compare(y);
        }
        private static unsafe int GetHashCode(byte[] obj)
        {
            var cbSize = obj.Length;
            var h1     = 0xCBF29CE484222325;
            fixed (byte* pb = obj)
            {
                var nb = pb;
                while (cbSize >= 8)
                {
                    h1     ^= *(ulong*) nb;
                    h1     *= 0x100000001B3;
                    nb     += 8;
                    cbSize -= 8;
                }
            }
            return (int) h1;
        }
        private struct Slot
        {
            public int    HashCode;
            public int    Next;
            public byte[] Value;
        }
    }
}