Int128 Bit Class
Jun-11,2021: Obsolete Use xIntX Instead.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential)]
[TypeConverter(typeof(Int128Converter))]
[DebuggerDisplay("{DDisplay}")]
public struct Int128 : IComparable<Int128>, IComparable, IEquatable<Int128>, IConvertible, IFormattable
{
public ulong _hi;
public ulong _lo;
private const ulong HiNeg = 0x8000000000000000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => "0x" + ToString("X1");
public static Int128 Zero;
public static Int128 Ten = new Int128(10);
public static Int128 One = new Int128(1);
public static Int128 MaxValue = GetMaxValue();
public static Int128 MinValue = GetMinValue();
private static Int128 GetMaxValue()
{
return new Int128(long.MaxValue, ulong.MaxValue);
}
private static Int128 GetMinValue()
{
return new Int128(0x8000000000000000, 0);
}
/// <summary>
/// Either base10 or base 16
/// </summary>
public Int128(string value)
{
TryParse(value, out var result);
_hi = result._hi;
_lo = result._lo;
}
public Int128(byte value)
{
_hi = 0;
_lo = value;
}
public Int128(bool value)
{
_hi = 0;
_lo = (ulong) (value ? 1 : 0);
}
public Int128(char value)
{
_hi = 0;
_lo = value;
}
public Int128(decimal value)
{
if (value < 0)
{
var n = -new Int128(-value);
_hi = n._hi;
_lo = n._lo;
return;
}
var bits = decimal.GetBits(value);
_hi = (uint) bits[2];
_lo = (uint) bits[0] | ((ulong) bits[1] << 32);
}
public Int128(double value)
: this((decimal) value)
{
}
public Int128(float value)
: this((decimal) value)
{
}
public Int128(short value)
{
if (value < 0)
{
var n = -new Int128(-(value + 1)) - 1;
_hi = n._hi;
_lo = n._lo;
return;
}
_hi = 0;
_lo = (ulong) value;
}
public Int128(int value)
{
if (value < 0)
{
var n = -new Int128(-(value + 1)) - 1;
_hi = n._hi;
_lo = n._lo;
return;
}
_hi = 0;
_lo = (ulong) value;
}
public Int128(long value)
{
if (value < 0)
{
var n = -new Int128(-(value + 1)) - 1;
_hi = n._hi;
_lo = n._lo;
return;
}
_hi = 0;
_lo = (ulong) value;
}
public Int128(sbyte value)
{
if (value < 0)
{
var n = -new Int128(-(value + 1)) - 1;
_hi = n._hi;
_lo = n._lo;
return;
}
_hi = 0;
_lo = (ulong) value;
}
public Int128(ushort value)
{
_hi = 0;
_lo = value;
}
public Int128(uint value)
{
_hi = 0;
_lo = value;
}
public Int128(ulong value)
{
_hi = 0;
_lo = value;
}
public Int128(Guid value)
: this(value.ToByteArray())
{
}
public Int128(byte[] value)
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Length != 16)
throw new ArgumentException(null, "value");
_hi = BitConverter.ToUInt64(value, 8);
_lo = BitConverter.ToUInt64(value, 0);
}
public Int128(ulong hi, ulong lo)
{
_hi = hi;
_lo = lo;
}
public Int128(int sign, uint[] ints)
{
if (ints == null)
throw new ArgumentNullException("ints");
var lo = new byte[8];
var hi = new byte[8];
if (ints.Length > 0)
{
Array.Copy(BitConverter.GetBytes(ints[0]), 0, lo, 0, 4);
if (ints.Length > 1)
{
Array.Copy(BitConverter.GetBytes(ints[1]), 0, lo, 4, 4);
if (ints.Length > 2)
{
Array.Copy(BitConverter.GetBytes(ints[2]), 0, hi, 0, 4);
if (ints.Length > 3)
Array.Copy(BitConverter.GetBytes(ints[3]), 0, hi, 4, 4);
}
}
}
_lo = BitConverter.ToUInt64(lo, 0);
_hi = BitConverter.ToUInt64(hi, 0);
if (sign < 0)
_hi |= HiNeg;
else
_hi &= ~HiNeg;
}
public int BitWidth
{
get
{
Int128 bitWidth = 1;
var v = this;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
while (bitWidth % 8 != 0)
bitWidth++;
return (int) bitWidth;
}
}
public int Sign
{
get
{
if (_hi == 0 && _lo == 0)
return 0;
return (_hi & HiNeg) == 0 ? 1 : -1;
}
}
public override int GetHashCode()
{
return _hi.GetHashCode() ^ _lo.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public bool Equals(Int128 obj)
{
return _hi == obj._hi && _lo == obj._lo;
}
public override string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
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 min;
int.TryParse(format.Substring(1).Trim(), out 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)));
}
private string ToHexString(bool caps, int min)
{
var sb = new StringBuilder();
var x = caps ? "X" : "x";
if (min < 0 || min > 16 || _hi != 0)
{
sb.Append(min > 16 ? _hi.ToString(x + (min - 16)) : _hi.ToString(x));
sb.Append(_lo.ToString(x + "16"));
}
else
{
sb.Append(_lo.ToString(x + min));
}
return sb.ToString();
}
private string ToString(NumberFormatInfo info)
{
if (Sign == 0)
return "0";
var sb = new StringBuilder();
var current = this;
current._hi &= ~HiNeg;
Int128 r;
while (true)
{
current = DivRem(current, Ten, out r);
if (r._lo > 0 || current.Sign != 0 || sb.Length == 0)
sb.Insert(0, (char) ('0' + r._lo));
if (current.Sign == 0)
break;
}
var s = sb.ToString();
if (Sign < 0 && s != "0")
return info.NegativeSign + s;
return s;
}
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 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 Int128 Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static Int128 Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static Int128 Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static Int128 Parse(string value, NumberStyles style, IFormatProvider provider)
{
Int128 result;
if (!TryParse(value, style, provider, out result))
throw new ArgumentException(null, "value");
return result;
}
public static bool TryParse(string value, out Int128 result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out Int128 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 TryParseHex(value, out result);
return TryParseNum(value, out result);
}
private static bool TryParseHex(string value, out Int128 result)
{
if (value.Length > 32)
throw new OverflowException();
result = Zero;
var hi = false;
var pos = 0;
for (var i = value.Length - 1; i >= 0; i--)
{
var ch = value[i];
ulong b;
if (ch >= '0' && ch <= '9')
b = (ulong) (ch - '0');
else if (ch >= 'A' && ch <= 'F')
b = (ulong) (ch - 'A' + 10);
else if (ch >= 'a' && ch <= 'f')
b = (ulong) (ch - 'a' + 10);
else
return false;
if (hi)
{
result._hi |= b << pos;
pos += 4;
}
else
{
result._lo |= b << pos;
pos += 4;
if (pos == 64)
{
pos = 0;
hi = true;
}
}
}
return true;
}
private static bool TryParseNum(string value, out Int128 result)
{
result = Zero;
foreach (var ch in value)
{
byte b;
if (ch >= '0' && ch <= '9')
b = (byte) (ch - '0');
else
return false;
result = Ten * result;
result += b;
}
return true;
}
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 (_hi != 0)
throw new OverflowException();
return Convert.ToUInt16(_lo);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
if (_hi != 0)
throw new OverflowException();
return Convert.ToUInt32(_lo);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
if (_hi != 0)
throw new OverflowException();
return _lo;
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public static int Compare(Int128 left, object right)
{
if (right is Int128)
return Compare(left, (Int128) right);
if (right is bool)
return Compare(left, new Int128((bool) right));
if (right is byte)
return Compare(left, new Int128((byte) right));
if (right is char)
return Compare(left, new Int128((char) right));
if (right is decimal)
return Compare(left, new Int128((decimal) right));
if (right is double)
return Compare(left, new Int128((double) right));
if (right is short)
return Compare(left, new Int128((short) right));
if (right is int)
return Compare(left, new Int128((int) right));
if (right is long)
return Compare(left, new Int128((long) right));
if (right is sbyte)
return Compare(left, new Int128((sbyte) right));
if (right is float)
return Compare(left, new Int128((float) right));
if (right is ushort)
return Compare(left, new Int128((ushort) right));
if (right is uint)
return Compare(left, new Int128((uint) right));
if (right is ulong)
return Compare(left, new Int128((ulong) right));
var bytes = right as byte[];
if (bytes != null && bytes.Length != 16)
return Compare(left, new Int128(bytes));
if (right is Guid)
return Compare(left, new Int128((Guid) right));
throw new ArgumentException();
}
public byte[] ToByteArray()
{
var bytes = new byte[16];
Buffer.BlockCopy(BitConverter.GetBytes(_lo), 0, bytes, 0, 8);
Buffer.BlockCopy(BitConverter.GetBytes(_hi), 0, bytes, 8, 8);
return bytes;
}
public static int Compare(Int128 left, Int128 right)
{
if (left.Sign < 0)
{
if (right.Sign >= 0)
return -1;
var xhi = left._hi & ~HiNeg;
var yhi = right._hi & ~HiNeg;
if (xhi != yhi)
return -xhi.CompareTo(yhi);
return -left._lo.CompareTo(right._lo);
}
if (right.Sign < 0)
return 1;
if (left._hi != right._hi)
return left._hi.CompareTo(right._hi);
return left._lo.CompareTo(right._lo);
}
public int CompareTo(Int128 value)
{
return Compare(this, value);
}
public static implicit operator Int128(bool value)
{
return new Int128(value);
}
public static implicit operator Int128(byte value)
{
return new Int128(value);
}
public static implicit operator Int128(char value)
{
return new Int128(value);
}
public static explicit operator Int128(decimal value)
{
return new Int128(value);
}
public static explicit operator Int128(double value)
{
return new Int128(value);
}
public static implicit operator Int128(short value)
{
return new Int128(value);
}
public static implicit operator Int128(int value)
{
return new Int128(value);
}
public static implicit operator Int128(long value)
{
return new Int128(value);
}
public static implicit operator Int128(sbyte value)
{
return new Int128(value);
}
public static explicit operator Int128(float value)
{
return new Int128(value);
}
public static implicit operator Int128(ushort value)
{
return new Int128(value);
}
public static implicit operator Int128(uint value)
{
return new Int128(value);
}
public static implicit operator Int128(ulong value)
{
return new Int128(value);
}
public static explicit operator bool(Int128 value)
{
return value.Sign != 0;
}
public static explicit operator byte(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value.Sign < 0 || value._lo > 0xFF)
throw new OverflowException();
return (byte) value._lo;
}
public static explicit operator char(Int128 value)
{
if (value.Sign == 0)
return (char) 0;
if (value.Sign < 0 || value._lo > 0xFFFF)
throw new OverflowException();
return (char) (ushort) value._lo;
}
public static explicit operator decimal(Int128 value)
{
if (value.Sign == 0)
return 0;
return new decimal((int) (value._lo & 0xFFFFFFFF), (int) (value._lo >> 32), (int) (value._hi & 0xFFFFFFFF),
value.Sign < 0, 0);
}
public static explicit operator double(Int128 value)
{
if (value.Sign == 0)
return 0;
double d;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!double.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out d))
throw new OverflowException();
return d;
}
public static explicit operator float(Int128 value)
{
if (value.Sign == 0)
return 0;
float f;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!float.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out f))
throw new OverflowException();
return f;
}
public static explicit operator short(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value._lo > 0x8000)
throw new OverflowException();
if (value._lo == 0x8000 && value.Sign > 0)
throw new OverflowException();
return (short) ((int) value._lo * value.Sign);
}
public static explicit operator int(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value._lo > 0x80000000)
throw new OverflowException();
if (value._lo == 0x80000000 && value.Sign > 0)
throw new OverflowException();
return (int) value._lo * value.Sign;
}
public static explicit operator long(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value._lo > long.MaxValue)
throw new OverflowException();
return (long) value._lo * value.Sign;
}
public static explicit operator uint(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value.Sign < 0 || value._lo > uint.MaxValue)
throw new OverflowException();
return (uint) value._lo;
}
public static explicit operator ushort(Int128 value)
{
if (value.Sign == 0)
return 0;
if (value.Sign < 0 || value._lo > ushort.MaxValue)
throw new OverflowException();
return (ushort) value._lo;
}
public static explicit operator ulong(Int128 value)
{
if (value.Sign < 0 || value._hi != 0)
throw new OverflowException();
return value._lo;
}
public static bool operator >(Int128 left, Int128 right)
{
return Compare(left, right) > 0;
}
public static bool operator <(Int128 left, Int128 right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(Int128 left, Int128 right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(Int128 left, Int128 right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(Int128 left, Int128 right)
{
return Compare(left, right) != 0;
}
public static bool operator ==(Int128 left, Int128 right)
{
return Compare(left, right) == 0;
}
public static Int128 operator +(Int128 value)
{
return value;
}
public static Int128 operator -(Int128 value)
{
return Negate(value);
}
public static Int128 operator ~(Int128 value)
{
return -(value + One);
}
public static Int128 operator ++(Int128 value)
{
return value + 1;
}
public static Int128 operator --(Int128 value)
{
return value - 1;
}
public static Int128 Negate(Int128 value)
{
return new Int128(~value._hi, ~value._lo) + 1;
}
public Int128 ToAbs()
{
return Abs(this);
}
public static Int128 Abs(Int128 value)
{
if (value.Sign < 0)
return -value;
return value;
}
public static Int128 operator +(Int128 left, Int128 right)
{
var add = left;
add._hi += right._hi;
add._lo += right._lo;
if (add._lo < left._lo)
add._hi++;
return add;
}
public static Int128 operator -(Int128 left, Int128 right)
{
return left + -right;
}
public static Int128 Add(Int128 left, Int128 right)
{
return left + right;
}
public static Int128 Subtract(Int128 left, Int128 right)
{
return left - right;
}
public static Int128 Divide(Int128 dividend, Int128 divisor)
{
Int128 integer;
return DivRem(dividend, divisor, out integer);
}
public static Int128 DivRem(Int128 dividend, Int128 divisor, out Int128 remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
uint[] quotient;
uint[] rem;
DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out quotient, out rem);
remainder = new Int128(1, rem);
return new Int128(dividend.Sign * divisor.Sign, 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);
long di = 0;
long 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 = (long) dqj - dj;
index++;
}
dj = normDividend[j + divisorLen] - di;
normDividend[j + divisorLen] = (uint) dj;
quotient[j] = (uint) qj;
if (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[0];
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 unormalized = new uint[len];
if (shift > 0)
{
var rshift = 32 - shift;
uint r = 0;
for (var i = len - 1; i >= 0; i--)
{
unormalized[i] = (normalized[i] >> shift) | r;
r = normalized[i] << rshift;
}
}
else
{
for (var j = 0; j < len; j++)
unormalized[j] = normalized[j];
}
return unormalized;
}
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 Int128 Remainder(Int128 dividend, Int128 divisor)
{
Int128 remainder;
DivRem(dividend, divisor, out remainder);
return remainder;
}
public static Int128 operator %(Int128 dividend, Int128 divisor)
{
return Remainder(dividend, divisor);
}
public static Int128 operator /(Int128 dividend, Int128 divisor)
{
return Divide(dividend, divisor);
}
public ulong[] ToUIn64Array()
{
return new[] {_hi, _lo};
}
public uint[] ToUIn32Array()
{
var ints = new uint[4];
var lob = BitConverter.GetBytes(_lo);
var hib = BitConverter.GetBytes(_hi);
Buffer.BlockCopy(lob, 0, ints, 0, 4);
Buffer.BlockCopy(lob, 4, ints, 4, 4);
Buffer.BlockCopy(hib, 0, ints, 8, 4);
Buffer.BlockCopy(hib, 4, ints, 12, 4);
return ints;
}
public static Int128 Multiply(Int128 left, Int128 right)
{
var xInts = left.ToUIn32Array();
var yInts = right.ToUIn32Array();
var mulInts = new uint[8];
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 Int128(left.Sign * right.Sign, mulInts);
}
public static Int128 operator *(Int128 left, Int128 right)
{
return Multiply(left, right);
}
public static Int128 operator >>(Int128 value, int shift)
{
if (shift == 0)
return value;
if (shift < 0)
return value << -shift;
shift = shift % 128;
var shifted = new Int128();
if (shift > 63)
{
shifted._lo = value._hi >> (shift - 64);
shifted._hi = 0;
}
else
{
shifted._hi = value._hi >> shift;
shifted._lo = (value._hi << (64 - shift)) | (value._lo >> shift);
}
return shifted;
}
public static Int128 operator <<(Int128 value, int shift)
{
if (shift == 0)
return value;
if (shift < 0)
return value >> -shift;
shift = shift % 128;
var shifted = new Int128();
if (shift > 63)
{
shifted._hi = value._lo << (shift - 64);
shifted._lo = 0;
}
else
{
var ul = value._lo >> (64 - shift);
shifted._hi = ul | (value._hi << shift);
shifted._lo = value._lo << shift;
}
return shifted;
}
public static Int128 operator |(Int128 left, Int128 right)
{
if (left == 0)
return right;
if (right == 0)
return left;
var x = left.ToUIn32Array();
var y = right.ToUIn32Array();
var z = new uint[Math.Max(x.Length, y.Length)];
var xExtend = left.Sign < 0 ? uint.MaxValue : 0;
var yExtend = right.Sign < 0 ? uint.MaxValue : 0;
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : xExtend;
var yu = i < y.Length ? y[i] : yExtend;
z[i] = xu | yu;
}
return new Int128(left.Sign * right.Sign, z);
}
public static Int128 operator ^(Int128 left, Int128 right)
{
var x = left.ToUIn32Array();
var y = right.ToUIn32Array();
var z = new uint[Math.Max(x.Length, y.Length)];
var xExtend = left.Sign < 0 ? uint.MaxValue : 0;
var yExtend = right.Sign < 0 ? uint.MaxValue : 0;
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : xExtend;
var yu = i < y.Length ? y[i] : yExtend;
z[i] = xu ^ yu;
}
return new Int128(left.Sign * right.Sign, z);
}
public static Int128 operator &(Int128 left, Int128 right)
{
if (left == 0 || right == 0)
return Zero;
var x = left.ToUIn32Array();
var y = right.ToUIn32Array();
var z = new uint[Math.Max(x.Length, y.Length)];
var xExtend = left.Sign < 0 ? uint.MaxValue : 0;
var yExtend = right.Sign < 0 ? uint.MaxValue : 0;
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : xExtend;
var yu = i < y.Length ? y[i] : yExtend;
z[i] = xu & yu;
}
return new Int128(left.Sign * right.Sign, z);
}
public class Int128Converter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value != null)
{
Int128 i;
if (TryParse($"{value}", out i))
return i;
}
return new Int128();
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value,
Type destinationType)
{
if (destinationType == typeof(string))
return $"{value}";
return base.ConvertTo(context, culture, value, destinationType);
}
}
}