FixedUIntXInt.cs

Variable Bit Width Unsigned Integer Class

[Serializable]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    [TypeConverter(typeof(FixedUIntXIntConverter))]
    [DebuggerDisplay("{DDisplay}")]
    public class FixedUIntXInt : IComparable<FixedUIntXInt>, IComparable, IEquatable<FixedUIntXInt>, IConvertible, IFormattable
    {
        private const  int           DataSize      = sizeof(uint);
        private static int           DataBitLength = 64; //<--Change Here
        private static int           DataLength    = DataBitLength >> 5;
        public static  FixedUIntXInt Zero          = new FixedUIntXInt("0",  64);
        public static  FixedUIntXInt Ten           = new FixedUIntXInt("10", 64);
        public static  FixedUIntXInt One           = new FixedUIntXInt("1",  64);
        private        uint[]        data;
        public FixedUIntXInt(FixedUIntXInt value)
        {
            if (value == null)
                return;
            CalculateMinDataLength(value.data.Length);
            data = new uint[DataLength];
            value.data.CopyTo(data, 0);
        }
        public FixedUIntXInt(string value, int orBl)
        {
            if (orBl > 0)
                CalculateMinDataLength(orBl >> 5);
            if (!TryParse(value, out var result))
                throw new Exception("TryParse Failed.");
            data = new uint[DataLength];
            result.data.CopyTo(data, 0);
        }
        public FixedUIntXInt(byte value)
        {
            data    = new uint[DataLength];
            data[0] = value;
        }
        public FixedUIntXInt(bool value)
        {
            data    = new uint[DataLength];
            data[0] = (uint) (value ? 1 : 0);
        }
        public FixedUIntXInt(char value)
        {
            data    = new uint[DataLength];
            data[0] = value;
        }
        public FixedUIntXInt(BigDecimal value)
        {
            var ba = value.UnscaledValue.ToByteArray();
            data = new uint[DataLength];
            for (var i = 0; i < DataLength; i++)
                data[i] = BitConverter.ToUInt32(ba, i * DataSize);
        }
        public FixedUIntXInt(decimal value)
        {
            data = new uint[DataLength];
            if (value < 0)
            {
                var n = -new FixedUIntXInt(-value);
                n.data.CopyTo(data, 0);
                return;
            }
            var bits = decimal.GetBits(value);
            data[2] = (uint) bits[2];
            data[1] = (uint) bits[1];
            data[0] = (uint) bits[0];
        }
        public FixedUIntXInt(double value) : this((decimal) value)
        {
        }
        public FixedUIntXInt(float value) : this((decimal) value)
        {
        }
        public FixedUIntXInt(short value) : this((int) value)
        {
        }
        public FixedUIntXInt(int value)
        {
            data    = new uint[DataLength];
            data[0] = (uint) value;
        }
        public FixedUIntXInt(long value) : this((ulong) value)
        {
        }
        public FixedUIntXInt(sbyte value) : this((int) value)
        {
        }
        public FixedUIntXInt(ushort value)
        {
            data    = new uint[DataLength];
            data[0] = value;
        }
        public FixedUIntXInt(uint value)
        {
            data    = new uint[DataLength];
            data[0] = value;
        }
        public FixedUIntXInt(ulong value)
        {
            data    = new uint[DataLength];
            data[0] = (uint) ((value >> 32) & 0xffffffff);
            data[1] = (uint) (value         & 0xffffffff);
        }
        public FixedUIntXInt(BigInteger value) : this(value.ToByteArray())
        {
        }
        public FixedUIntXInt(Guid value) : this(value.ToByteArray())
        {
        }
        public FixedUIntXInt(byte[] value)
        {
            var minSize = value.Length / DataSize;
            if (value == null)
                throw new ArgumentNullException("value");
            var byteCount      = value.Length;
            var isNegative     = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
            var unalignedBytes = byteCount % DataSize;
            var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
            data = new uint[Math.Max(dwordCount, minSize)];
            if (byteCount == 0)
                return;
            int curDword, curByte, byteInDword;
            curByte = 3;
            for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
            {
                byteInDword = 0;
                while (byteInDword < DataSize)
                {
                    data[curDword] <<= 8;
                    data[curDword] |=  value[curByte];
                    curByte--;
                    byteInDword++;
                }
                curByte += 8;
            }
            if (unalignedBytes != 0)
            {
                if (isNegative)
                    data[dwordCount - 1] = 0xffffffff;
                for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
                {
                    data[curDword] <<= 8;
                    data[curDword] |=  value[curByte];
                }
            }
        }
        public FixedUIntXInt(uint[] array)
        {
            if (array == null)
                throw new Exception("Array cannot be null.");
            if (array.Length != DataLength)
                Array.Resize(ref array, DataLength);
            data = new uint[DataLength];
            var ba = new byte[4];
            for (var i = 0; i < DataLength; i++)
            {
                Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
                data[i] = BitConverter.ToUInt32(ba, 0);
            }
        }
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string DDisplay => ToString();
        public FixedUIntXInt MaxValue => (One << DataBitLength) - 1;
        public int SizeOfDataUsed
        {
            get
            {
                var dataUsed = data.Length;
                while (dataUsed > 1 && data[dataUsed - 1] == 0)
                    --dataUsed;
                if (dataUsed == 0)
                    dataUsed = 1;
                return dataUsed;
            }
        }
        public int ResetDataBitLength
        {
            get => DataBitLength;
            set
            {
                if (DataBitLength == value)
                    return;
                DataBitLength = value;
                DataLength    = DataBitLength >> 5;
                Array.Resize(ref data, DataLength);
            }
        }
        public int BitWidth
        {
            get
            {
                FixedUIntXInt bw = 1;
                var           v  = this;
                while ((v >>= 1) > 0)
                    bw++;
                if (bw < 8)
                    bw = 8;
                while (bw % 8 != 0)
                    bw++;
                return (int) bw;
            }
        }
        public bool IsOne  => this     == One;
        public bool IsEven => this % 2 == 0;
        public bool IsZero
        {
            get
            {
                for (var i = 0; i < data.Length; i++)
                    if (data[i] != 0)
                        return false;
                return true;
            }
        }
        int IComparable.CompareTo(object obj)
        {
            return Compare(this, obj);
        }
        public int CompareTo(FixedUIntXInt value)
        {
            return Compare(this, value);
        }
        TypeCode IConvertible.GetTypeCode()
        {
            return TypeCode.Object;
        }
        bool IConvertible.ToBoolean(IFormatProvider provider)
        {
            return (bool) this;
        }
        byte IConvertible.ToByte(IFormatProvider provider)
        {
            return (byte) this;
        }
        char IConvertible.ToChar(IFormatProvider provider)
        {
            return (char) this;
        }
        DateTime IConvertible.ToDateTime(IFormatProvider provider)
        {
            throw new InvalidCastException();
        }
        decimal IConvertible.ToDecimal(IFormatProvider provider)
        {
            return (decimal) this;
        }
        double IConvertible.ToDouble(IFormatProvider provider)
        {
            return (double) this;
        }
        short IConvertible.ToInt16(IFormatProvider provider)
        {
            return (short) this;
        }
        int IConvertible.ToInt32(IFormatProvider provider)
        {
            return (int) this;
        }
        long IConvertible.ToInt64(IFormatProvider provider)
        {
            return (int) this;
        }
        sbyte IConvertible.ToSByte(IFormatProvider provider)
        {
            return (sbyte) this;
        }
        float IConvertible.ToSingle(IFormatProvider provider)
        {
            return (float) this;
        }
        string IConvertible.ToString(IFormatProvider provider)
        {
            return ToString(null, provider);
        }
        public object ToType(Type conversionType, IFormatProvider provider)
        {
            object value;
            if (TryConvert(conversionType, provider, out value))
                return value;
            throw new InvalidCastException();
        }
        ushort IConvertible.ToUInt16(IFormatProvider provider)
        {
            if (data[1] != 0)
                throw new OverflowException();
            return Convert.ToUInt16(data[0]);
        }
        uint IConvertible.ToUInt32(IFormatProvider provider)
        {
            if (data[1] != 0)
                throw new OverflowException();
            return Convert.ToUInt32(data[0]);
        }
        ulong IConvertible.ToUInt64(IFormatProvider provider)
        {
            if (data[1] != 0)
                throw new OverflowException();
            return data[0];
        }
        public bool Equals(FixedUIntXInt obj)
        {
            if (ReferenceEquals(obj, null))
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            if (data.Length != obj.data.Length)
            {
                var len = Math.Max(data.Length, obj.data.Length);
                if (data.Length < len)
                {
                    var tData = new uint[len];
                    Array.Copy(data, 0, tData, 0, data.Length);
                    data = tData;
                }
                if (obj.data.Length < len)
                    Resize(ref obj, len);
            }
            for (var i = 0; i < data.Length; i++)
                if (data[i] != obj.data[i])
                    return false;
            return true;
        }
        public string ToString(string format, IFormatProvider formatProvider)
        {
            if (formatProvider == null)
                formatProvider = CultureInfo.CurrentCulture;
            if (!string.IsNullOrEmpty(format))
            {
                var ch = format[0];
                if (ch == 'x' || ch == 'X')
                {
                    int.TryParse(format.Substring(1).Trim(), out var min);
                    return ToHexString(ch == 'X', min);
                }
                if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
                    throw new NotSupportedException("Not supported format: " + format);
            }
            return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
        }
        private static void CalculateMinDataLength(int minSize)
        {
            if (minSize != DataLength)
            {
                DataBitLength = 32 * minSize;
                DataLength    = minSize;
            }
        }
        private static byte[] ToByteArray(ulong[] value)
        {
            var ba = new byte[value.Length << 3];
            Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
            return ba;
        }
        private static byte[] ToByteArray(uint[] value)
        {
            var ba = new byte[value.Length << 2];
            Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
            return ba;
        }
        public override int GetHashCode()
        {
            var hash = 0x811c9dc5;
            for (var i = 0; i < DataLength; i++)
            {
                hash ^= ((hash << 13) | (hash >> 19)) ^ data[i];
                hash *= 0x1000193;
            }
            return (int) hash;
        }
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
        public override string ToString()
        {
            return ToString(null, null);
        }
        public string ToString(string format)
        {
            return ToString(format, null);
        }
        private string ToHexString(bool caps, int min)
        {
            var bytes = ToByteArray().Invert();
            var sb    = new StringBuilder();
            var x     = caps ? "X" : "x";
            foreach (var b in bytes)
            {
                var hex = b.ToString($"{x}2");
                sb.Append(hex);
            }
            return sb.ToString();
        }
        private string ToString(NumberFormatInfo info, int radix)
        {
            if (radix < 2 || radix > 36)
                throw new ArgumentOutOfRangeException("radix");
            if (IsZero)
                return "0";
            var          a       = new FixedUIntXInt(this);
            var          biRadix = new FixedUIntXInt(radix);
            const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
            var          al      = new ArrayList();
            while (a > 0)
                try
                {
                    Divide(a, biRadix, out var remainder, out var quotient);
                    al.Insert(0, charSet[(int) remainder.data[0]]);
                    a = quotient;
                }
                catch (Exception ex)
                {
                    break;
                }
            var result = new string((char[]) al.ToArray(typeof(char)));
            return result;
        }
        public bool TryConvert(Type conversionType, IFormatProvider provider, out object value)
        {
            if (conversionType == typeof(bool))
            {
                value = (bool) this;
                return true;
            }
            if (conversionType == typeof(byte))
            {
                value = (byte) this;
                return true;
            }
            if (conversionType == typeof(char))
            {
                value = (char) this;
                return true;
            }
            if (conversionType == typeof(decimal))
            {
                value = (decimal) this;
                return true;
            }
            if (conversionType == typeof(double))
            {
                value = (double) this;
                return true;
            }
            if (conversionType == typeof(short))
            {
                value = (short) this;
                return true;
            }
            if (conversionType == typeof(int))
            {
                value = (int) this;
                return true;
            }
            if (conversionType == typeof(long))
            {
                value = (long) this;
                return true;
            }
            if (conversionType == typeof(sbyte))
            {
                value = (sbyte) this;
                return true;
            }
            if (conversionType == typeof(float))
            {
                value = (float) this;
                return true;
            }
            if (conversionType == typeof(string))
            {
                value = ToString(null, provider);
                return true;
            }
            if (conversionType == typeof(ushort))
            {
                value = (ushort) this;
                return true;
            }
            if (conversionType == typeof(uint))
            {
                value = (uint) this;
                return true;
            }
            if (conversionType == typeof(ulong))
            {
                value = (ulong) this;
                return true;
            }
            if (conversionType == typeof(byte[]))
            {
                value = ToByteArray();
                return true;
            }
            if (conversionType == typeof(Guid))
            {
                value = new Guid(ToByteArray());
                return true;
            }
            value = null;
            return false;
        }
        public static FixedUIntXInt Parse(string value)
        {
            return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
        }
        public static FixedUIntXInt Parse(string value, NumberStyles style)
        {
            return Parse(value, style, NumberFormatInfo.CurrentInfo);
        }
        public static FixedUIntXInt Parse(string value, IFormatProvider provider)
        {
            return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
        }
        public static FixedUIntXInt 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 FixedUIntXInt result)
        {
            return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
        }
        public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out FixedUIntXInt result)
        {
            result = Zero;
            if (string.IsNullOrEmpty(value))
                return false;
            if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
            {
                style |= NumberStyles.AllowHexSpecifier;
                value =  value.Substring(1);
            }
            else
            {
                if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                {
                    style |= NumberStyles.AllowHexSpecifier;
                    value =  value.Substring(2);
                }
            }
            if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
                return TryParseNum(value, 16, out result);
            return TryParseNum(value, 10, out result);
        }
        public static bool TryParseNum(string digits, int radix, out FixedUIntXInt result)
        {
            result = new FixedUIntXInt(0);
            if (digits == null)
                return false;
            var multiplier = new FixedUIntXInt(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 int Compare(FixedUIntXInt left, object right)
        {
            if (right is FixedUIntXInt)
                return Compare(left, (FixedUIntXInt) right);
            if (right is bool)
                return Compare(left, new FixedUIntXInt((bool) right));
            if (right is byte)
                return Compare(left, new FixedUIntXInt((byte) right));
            if (right is char)
                return Compare(left, new FixedUIntXInt((char) right));
            if (right is decimal)
                return Compare(left, new FixedUIntXInt((decimal) right));
            if (right is double)
                return Compare(left, new FixedUIntXInt((double) right));
            if (right is short)
                return Compare(left, new FixedUIntXInt((short) right));
            if (right is int)
                return Compare(left, new FixedUIntXInt((int) right));
            if (right is long)
                return Compare(left, new FixedUIntXInt((long) right));
            if (right is sbyte)
                return Compare(left, new FixedUIntXInt((sbyte) right));
            if (right is float)
                return Compare(left, new FixedUIntXInt((float) right));
            if (right is ushort)
                return Compare(left, new FixedUIntXInt((ushort) right));
            if (right is uint)
                return Compare(left, new FixedUIntXInt((uint) right));
            if (right is ulong)
                return Compare(left, new FixedUIntXInt((ulong) right));
            var bytes = right as byte[];
            if (bytes != null && bytes.Length != 64)
                return Compare(left, new FixedUIntXInt(bytes));
            if (right is Guid)
                return Compare(left, new FixedUIntXInt((Guid) right));
            throw new ArgumentException();
        }
        public static int Compare(FixedUIntXInt left, FixedUIntXInt right)
        {
            MakeLikeLengths(ref left, ref 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 implicit operator FixedUIntXInt(bool value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(byte value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(char value)
        {
            return new FixedUIntXInt(value);
        }
        public static explicit operator FixedUIntXInt(decimal value)
        {
            return new FixedUIntXInt(value);
        }
        public static explicit operator FixedUIntXInt(double value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(short value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(int value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(long value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(sbyte value)
        {
            return new FixedUIntXInt(value);
        }
        public static explicit operator FixedUIntXInt(float value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(ushort value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(uint value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(ulong value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(BigInteger value)
        {
            return new FixedUIntXInt(value);
        }
        public static implicit operator FixedUIntXInt(BigDecimal value)
        {
            return new FixedUIntXInt(value);
        }
        public static explicit operator bool(FixedUIntXInt value)
        {
            return (byte) value.data[0] != 0;
        }
        public static explicit operator byte(FixedUIntXInt value)
        {
            return (byte) value.data[0];
        }
        public static explicit operator char(FixedUIntXInt value)
        {
            return (char) (ushort) value.data[0];
        }
        public static explicit operator decimal(FixedUIntXInt value)
        {
            return new decimal((int) value.data[0], (int) value.data[1], (int) value.data[2], false, 0);
        }
        public static explicit operator double(FixedUIntXInt value)
        {
            var nfi = CultureInfo.InvariantCulture.NumberFormat;
            if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
                throw new OverflowException();
            return d;
        }
        public static explicit operator float(FixedUIntXInt value)
        {
            var nfi = CultureInfo.InvariantCulture.NumberFormat;
            if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
                throw new OverflowException();
            return f;
        }
        public static explicit operator short(FixedUIntXInt value)
        {
            return (short) (int) value.data[0];
        }
        public static explicit operator int(FixedUIntXInt value)
        {
            return (int) value.data[0];
        }
        public static explicit operator long(FixedUIntXInt value)
        {
            if (value.data[1] != 0)
                return (long) (((ulong) value.data[1] << 32) | value.data[0]);
            return value.data[0];
        }
        public static explicit operator uint(FixedUIntXInt value)
        {
            return value.data[0];
        }
        public static explicit operator ushort(FixedUIntXInt value)
        {
            return (ushort) value.data[0];
        }
        public static explicit operator ulong(FixedUIntXInt value)
        {
            if (value.data[1] != 0)
                return ((ulong) value.data[1] << 32) | value.data[0];
            return value.data[0];
        }
        public static explicit operator BigInteger(FixedUIntXInt value)
        {
            return new BigInteger(value.ToByteArray());
        }
        public static bool operator >(FixedUIntXInt left, FixedUIntXInt right)
        {
            MakeLikeLengths(ref left, ref right);
            if (ReferenceEquals(left, null))
                throw new ArgumentNullException("left");
            if (ReferenceEquals(right, null))
                throw new ArgumentNullException("right");
            for (var i = 0; i < DataLength; i++)
                if (left.data[i] != right.data[i])
                    return left.data[i] > right.data[i];
            return false;
        }
        private static void MakeLikeLengths(ref FixedUIntXInt left, ref FixedUIntXInt right)
        {
            if (left.data.Length != right.data.Length)
            {
                var len = Math.Max(left.data.Length, right.data.Length);
                Resize(ref left,  len);
                Resize(ref right, len);
            }
        }
        private static void Resize(ref FixedUIntXInt value, int newSize)
        {
            var nData = new uint[newSize];
            var len   = value.data.Length;
            for (var i = 0; i < len; i++)
                nData[i] = value.data[i];
            value.data = (uint[]) nData.Clone();
        }
        public static bool operator <(FixedUIntXInt left, FixedUIntXInt right)
        {
            return Compare(left, right) < 0;
        }
        public static bool operator >=(FixedUIntXInt left, FixedUIntXInt right)
        {
            return Compare(left, right) >= 0;
        }
        public static bool operator <=(FixedUIntXInt left, FixedUIntXInt right)
        {
            return Compare(left, right) <= 0;
        }
        public static bool operator !=(FixedUIntXInt left, FixedUIntXInt right)
        {
            return Compare(left, right) != 0;
        }
        public static bool operator ==(FixedUIntXInt left, FixedUIntXInt right)
        {
            if (ReferenceEquals(left, right))
                return true;
            if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
                return false;
            return left.Equals(right);
        }
        public static FixedUIntXInt operator +(FixedUIntXInt value)
        {
            return value;
        }
        public static FixedUIntXInt operator ~(FixedUIntXInt value)
        {
            var da = new uint[DataLength];
            for (var idx = 0; idx < DataLength; idx++)
                da[idx] = ~value.data[idx];
            return new FixedUIntXInt(da);
        }
        public static FixedUIntXInt operator -(FixedUIntXInt value)
        {
            return Negate(value);
        }
        public static FixedUIntXInt operator ++(FixedUIntXInt value)
        {
            return value + 1;
        }
        public static FixedUIntXInt operator --(FixedUIntXInt value)
        {
            return value - 1;
        }
        public static FixedUIntXInt Negate(FixedUIntXInt value)
        {
            for (var i = 0; i < DataLength; i++)
                value.data[i] = ~value.data[i];
            return new FixedUIntXInt(value);
        }
        public static FixedUIntXInt operator +(FixedUIntXInt left, FixedUIntXInt right)
        {
            var  dl     = left.data.Length > right.data.Length ? left.data.Length : right.data.Length;
            var  result = new uint[dl];
            long carry  = 0;
            for (var i = 0; i < dl; i++)
            {
                var sum = left.data[i] + (long) right.data[i] + carry;
                carry     = sum >> 32;
                result[i] = (uint) (sum & 0xFFFFFFFF);
            }
            if (carry != 0)
            {
                var idx = 0;
                while (idx < result.Length - 1)
                {
                    if (result[idx] == 0)
                        break;
                    idx++;
                }
                result[idx] = (uint) carry;
            }
            return new FixedUIntXInt(result);
        }
        public static FixedUIntXInt operator -(FixedUIntXInt left, FixedUIntXInt right)
        {
            var  size  = Math.Max(left.data.Length, right.data.Length) + 1;
            var  da    = new uint[size];
            long carry = 0;
            for (var i = 0; i < da.Length - 1; i++)
            {
                var diff = left.data[i] - (long) right.data[i] - carry;
                da[i] = (uint) (diff & DigitsArray.AllBits);
                carry = diff < 0 ? 1 : 0;
            }
            return new FixedUIntXInt(da);
        }
        public static FixedUIntXInt Add(FixedUIntXInt left, FixedUIntXInt right)
        {
            return left + right;
        }
        public static FixedUIntXInt Subtract(FixedUIntXInt left, FixedUIntXInt right)
        {
            return left - right;
        }
        public static FixedUIntXInt Divide(FixedUIntXInt dividend, FixedUIntXInt divisor)
        {
            return DivRem(dividend, divisor, out var integer);
        }
        public static void Divide(FixedUIntXInt dividend, FixedUIntXInt divisor, out FixedUIntXInt remainder, out FixedUIntXInt quotient)
        {
            if (divisor == 0)
                throw new DivideByZeroException();
            DivRem(dividend.data, divisor.data, out var quo, out var rem);
            remainder = new FixedUIntXInt(rem);
            quotient  = new FixedUIntXInt(quo);
        }
        public static FixedUIntXInt DivRem(FixedUIntXInt dividend, FixedUIntXInt divisor, out FixedUIntXInt remainder)
        {
            if (divisor == 0)
                throw new DivideByZeroException();
            DivRem(dividend.data, divisor.data, out var quotient, out var rem);
            remainder = new FixedUIntXInt(rem);
            return new FixedUIntXInt(quotient);
        }
        private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
        {
            const ulong hiBit       = 0x100000000;
            var         divisorLen  = GetLength(divisor);
            var         dividendLen = GetLength(dividend);
            if (divisorLen <= 1)
            {
                ulong rem = 0;
                var   div = divisor[0];
                quotient  = new uint[dividendLen];
                remainder = new uint[1];
                for (var i = dividendLen - 1; i >= 0; i--)
                {
                    rem *= hiBit;
                    rem += dividend[i];
                    var q = rem / div;
                    rem         -= q * div;
                    quotient[i] =  (uint) q;
                }
                remainder[0] = (uint) rem;
                return;
            }
            if (dividendLen >= divisorLen)
            {
                var shift        = GetNormalizeShift(divisor[divisorLen - 1]);
                var normDividend = new uint[dividendLen + 1];
                var normDivisor  = new uint[divisorLen];
                Normalize(dividend, dividendLen, normDividend, shift);
                Normalize(divisor,  divisorLen,  normDivisor,  shift);
                quotient = new uint[dividendLen - divisorLen + 1];
                for (var j = dividendLen - divisorLen; j >= 0; j--)
                {
                    var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
                    var qj = dx / normDivisor[divisorLen                                        - 1];
                    dx -= qj * normDivisor[divisorLen - 1];
                    do
                    {
                        if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
                            break;
                        qj -= 1L;
                        dx += normDivisor[divisorLen - 1];
                    } while (dx < hiBit);
                    ulong di = 0;
                    ulong dj;
                    var   index = 0;
                    while (index < divisorLen)
                    {
                        var dqj = normDivisor[index] * qj;
                        dj                      = normDividend[index + j] - (uint) dqj - di;
                        normDividend[index + j] = (uint) dj;
                        dqj                     = dqj >> 32;
                        dj                      = dj  >> 32;
                        di                      = dqj - dj;
                        index++;
                    }
                    dj                           = normDividend[j + divisorLen] - di;
                    normDividend[j + divisorLen] = (uint) dj;
                    quotient[j]                  = (uint) qj;
                    if ((long) dj < 0)
                    {
                        quotient[j]--;
                        ulong sum = 0;
                        for (index = 0; index < divisorLen; index++)
                        {
                            sum                     = normDivisor[index] + normDividend[j + index] + sum;
                            normDividend[j + index] = (uint) sum;
                            sum                     = sum >> 32;
                        }
                        sum += normDividend[j + divisorLen];
                        normDividend[j        + divisorLen] = (uint) sum;
                    }
                }
                remainder = Unnormalize(normDividend, shift);
                return;
            }
            quotient  = new uint[1];
            remainder = dividend;
        }
        private static int GetLength(uint[] uints)
        {
            var index = uints.Length - 1;
            while (index >= 0 && uints[index] == 0)
                index--;
            return index + 1;
        }
        private static int GetNormalizeShift(uint ui)
        {
            var shift = 0;
            if ((ui & 0xffff0000) == 0)
            {
                ui    =  ui << 16;
                shift += 16;
            }
            if ((ui & 0xff000000) == 0)
            {
                ui    =  ui << 8;
                shift += 8;
            }
            if ((ui & 0xf0000000) == 0)
            {
                ui    =  ui << 4;
                shift += 4;
            }
            if ((ui & 0xc0000000) == 0)
            {
                ui    =  ui << 2;
                shift += 2;
            }
            if ((ui & 0x80000000) == 0)
                shift++;
            return shift;
        }
        private static uint[] Unnormalize(uint[] normalized, int shift)
        {
            var len          = GetLength(normalized);
            var unnormalized = new uint[len];
            if (shift > 0)
            {
                var  rshift = 32 - shift;
                uint r      = 0;
                for (var i = len - 1; i >= 0; i--)
                {
                    unnormalized[i] = (normalized[i] >> shift) | r;
                    r               = normalized[i] << rshift;
                }
            }
            else
            {
                for (var j = 0; j < len; j++)
                    unnormalized[j] = normalized[j];
            }
            return unnormalized;
        }
        private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
        {
            int  i;
            uint n = 0;
            if (shift > 0)
            {
                var rShift = 32 - shift;
                for (i = 0; i < len; i++)
                {
                    normalized[i] = (unormalized[i] << shift) | n;
                    n             = unormalized[i] >> rShift;
                }
            }
            else
            {
                i = 0;
                while (i < len)
                {
                    normalized[i] = unormalized[i];
                    i++;
                }
            }
            while (i < normalized.Length)
                normalized[i++] = 0;
            if (n != 0)
                normalized[len] = n;
        }
        public static FixedUIntXInt Remainder(FixedUIntXInt dividend, FixedUIntXInt divisor)
        {
            DivRem(dividend, divisor, out var remainder);
            return remainder;
        }
        public static FixedUIntXInt Max(FixedUIntXInt left, FixedUIntXInt right)
        {
            return left.CompareTo(right) < 0 ? right : left;
        }
        public static FixedUIntXInt Min(FixedUIntXInt left, FixedUIntXInt right)
        {
            return left.CompareTo(right) <= 0 ? left : right;
        }
        public static int GetBitWidth(FixedUIntXInt n)
        {
            FixedUIntXInt bw = 1;
            var           v  = n;
            while ((v >>= 1) > 0)
                bw++;
            if (bw < 8)
                bw = 8;
            while (bw % 8 != 0)
                bw++;
            return (int) bw;
        }
        public static FixedUIntXInt operator %(FixedUIntXInt dividend, FixedUIntXInt divisor)
        {
            return Remainder(dividend, divisor);
        }
        public static FixedUIntXInt operator /(FixedUIntXInt dividend, FixedUIntXInt divisor)
        {
            return Divide(dividend, divisor);
        }
        public ulong[] ToUIn64Array()
        {
            var al = data.Length >> 1;
            if (al * 2 != data.Length)
                al++;
            var arr = new ulong[al];
            Buffer.BlockCopy(data, 0, arr, 0, data.Length << 2);
            return arr;
        }
        public uint[] ToUIn32Array()
        {
            return data;
        }
        public byte[] ToByteArray()
        {
            var ba = new byte[data.Length * DataSize];
            Buffer.BlockCopy(data, 0, ba, 0, data.Length * DataSize);
            return ba;
        }
        public byte[] ToByteArray(int length)
        {
            if (length <= 0 || length > data.Length * DataSize)
                throw new ArgumentException($"Length {length} out of range length > 0 or length <= {data.Length * DataSize}");
            var ba = new byte[length];
            Buffer.BlockCopy(data, 0, ba, 0, length);
            return ba;
        }
        public static FixedUIntXInt Multiply(FixedUIntXInt left, FixedUIntXInt right)
        {
            var xInts   = left.data;
            var yInts   = right.data;
            var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
            for (var i = 0; i < xInts.Length; i++)
            {
                var   index     = i;
                ulong remainder = 0;
                foreach (var yi in yInts)
                {
                    remainder        = remainder + (ulong) xInts[i] * yi + mulInts[index];
                    mulInts[index++] = (uint) remainder;
                    remainder        = remainder >> 32;
                }
                while (remainder != 0)
                {
                    remainder        += mulInts[index];
                    mulInts[index++] =  (uint) remainder;
                    remainder        =  remainder >> 32;
                }
            }
            return new FixedUIntXInt(mulInts);
        }
        public static FixedUIntXInt operator *(FixedUIntXInt left, FixedUIntXInt right)
        {
            return Multiply(left, right);
        }
        public static FixedUIntXInt operator >>(FixedUIntXInt value, int shift)
        {
            if (value == Zero)
                return Zero;
            var xd          = (uint[]) value.data.Clone();
            var shiftAmount = 32;
            var invShift    = 0;
            var bufLen      = xd.Length;
            while (bufLen > 1 && xd[bufLen - 1] == 0)
                bufLen--;
            for (var count = shift; count > 0; count -= shiftAmount)
            {
                if (count < shiftAmount)
                {
                    shiftAmount = count;
                    invShift    = 32 - shiftAmount;
                }
                ulong carry = 0;
                for (var i = bufLen - 1; i >= 0; i--)
                {
                    var val = (ulong) xd[i] >> shiftAmount;
                    val   |= carry;
                    carry =  (ulong) xd[i] << invShift;
                    xd[i] =  (uint) val;
                }
            }
            return new FixedUIntXInt(xd);
        }
        public static FixedUIntXInt operator <<(FixedUIntXInt value, int shift)
        {
            if (value == Zero)
                return Zero;
            var digitShift = shift / 32;
            var smallShift = shift - digitShift * 32;
            var xd         = (uint[]) value.data.Clone();
            var xl         = xd.Length;
            var zd         = new uint[xl + digitShift + 1];
            if (smallShift == 0)
            {
                for (var index = 0; index < xl; ++index)
                    zd[index + digitShift] = xd[index];
            }
            else
            {
                var  carryShift = 32 - smallShift;
                uint carry      = 0;
                int  index;
                for (index = 0; index < xl; ++index)
                {
                    var rot = xd[index];
                    zd[index + digitShift] = (rot << smallShift) | carry;
                    carry                  = rot >> carryShift;
                }
                zd[index + digitShift] = carry;
            }
            return new FixedUIntXInt(zd);
        }
        public static FixedUIntXInt operator |(FixedUIntXInt left, FixedUIntXInt right)
        {
            if (left == 0)
                return right;
            if (right == 0)
                return left;
            var z = new uint[Math.Max(left.data.Length, right.data.Length)];
            for (var i = 0; i < z.Length; i++)
            {
                var xu = i < left.data.Length ? left.data[i] : 0U;
                var yu = i < right.data.Length ? right.data[i] : 0U;
                z[i] = xu | yu;
            }
            return new FixedUIntXInt(z);
        }
        public static FixedUIntXInt operator ^(FixedUIntXInt left, FixedUIntXInt right)
        {
            var z = new uint[Math.Max(left.data.Length, right.data.Length)];
            for (var i = 0; i < z.Length; i++)
            {
                var xu = i < left.data.Length ? left.data[i] : 0U;
                var yu = i < right.data.Length ? right.data[i] : 0U;
                z[i] = xu ^ yu;
            }
            return new FixedUIntXInt(z);
        }
        public static FixedUIntXInt operator &(FixedUIntXInt left, FixedUIntXInt right)
        {
            if (left == 0 || right == 0)
                return Zero;
            var z = new uint[Math.Max(left.data.Length, right.data.Length)];
            for (var i = 0; i < z.Length; i++)
            {
                var xu = i < left.data.Length ? left.data[i] : 0U;
                var yu = i < right.data.Length ? right.data[i] : 0U;
                z[i] = xu & yu;
            }
            return new FixedUIntXInt(z);
        }
        private class FixedUIntXIntConverter : 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 FixedUIntXInt("0", 64);
            }
            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);
            }
        }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *