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