Variable Bit Width Unsigned Integer Class
Jun-11,2021: Obsolete Use xIntX Instead.
[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); } } }