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