Int64 Bit Class
Jun-11,2021: Obsolete Use xIntX Instead.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// Used to verify correct mathematical operations before extending to 128, 256, 512, 1024, and 2048 versions
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(LongLongConverter))]
[DebuggerDisplay("{DDisplay}")]
public struct LongLong : IComparable<LongLong>, IComparable, IEquatable<LongLong>, IConvertible, IFormattable
{
private ulong B64;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public static LongLong Zero = new LongLong(0);
public static LongLong Ten = new LongLong(10);
public static LongLong One = new LongLong(1);
public static LongLong MaxValue = GetMaxValue();
public static LongLong MinValue = GetMinValue();
private static LongLong GetMaxValue()
{
return new LongLong(ulong.MaxValue);
}
private static LongLong GetMinValue()
{
return new LongLong(0);
}
public LongLong(LongLong value)
{
B64 = value.B64;
}
public LongLong(string value)
{
if (!TryParse(value, out var result))
throw new Exception("TryParse Failed.");
B64 = result.B64;
}
public LongLong(byte value)
{
B64 = value;
}
public LongLong(bool value)
{
B64 = (ulong) (value ? 1 : 0);
}
public LongLong(char value)
{
B64 = value;
}
public LongLong(decimal value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
var bits = decimal.GetBits(value);
B64 = (uint) bits[0];
if (value < 0)
B64 = ~B64;
}
public LongLong(double value) : this((decimal) value)
{
}
public LongLong(float value) : this((decimal) value)
{
}
public LongLong(short value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(int value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(long value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(UInt128 value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = value.B64;
}
public LongLong(sbyte value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(ushort value)
{
B64 = value;
}
public LongLong(uint value)
{
B64 = value;
}
public LongLong(ulong value)
{
B64 = value;
}
public LongLong(Guid value) : this(value.ToByteArray())
{
}
public LongLong(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
if (value.Length != 64)
throw new Exception("Values length must be 64 bytes.");
B64 = BitConverter.ToUInt64(value, 0);
}
//public LongLong(ulong b64)
//{
// B64 = b64;
//}
public LongLong(uint[] array)
{
if (array == null)
throw new Exception("Array cannot be null.");
var b64 = new byte[8];
if (array.Length > 0)
{
Array.Copy(BitConverter.GetBytes(array[0]), 0, b64, 0, 4);
if (array.Length > 1)
Array.Copy(BitConverter.GetBytes(array[1]), 0, b64, 4, 4);
}
B64 = BitConverter.ToUInt64(b64, 0);
}
public int BitWidth
{
get
{
LongLong bitWidth = 1;
var v = this;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
while (bitWidth % 8 != 0)
bitWidth++;
return (int) bitWidth;
}
}
public override int GetHashCode()
{
return B64.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public bool Equals(LongLong obj)
{
return B64 == obj.B64;
}
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.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)));
}
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)
{
var buf = ToByteArray();
var bi = new BigInteger(buf.Concat(new byte[] {0}).ToArray());
return bi.ToString();
}
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;
}
public ulong ToUInt64(IFormatProvider provider)
{
return (ulong) 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 LongLong Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static LongLong Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static LongLong Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static LongLong 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 LongLong result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out LongLong 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 LongLong result)
{
if (value.Length > 128)
throw new OverflowException();
result = Zero;
var pos = 0;
for (var i = value.Length - 1; i >= 0; i--)
{
var ch = value[i];
ulong bch;
if (ch >= '0' && ch <= '9')
bch = (ulong) (ch - '0');
else if (ch >= 'A' && ch <= 'F')
bch = (ulong) (ch - 'A' + 10);
else if (ch >= 'a' && ch <= 'f')
bch = (ulong) (ch - 'a' + 10);
else
return false;
if (pos < 64)
result.B64 |= bch << pos;
pos += 4;
}
return true;
}
private static bool TryParseNum(string value, out LongLong 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)
{
return Convert.ToUInt16(B64);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
return Convert.ToUInt32(B64);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
return B64;
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public static int Compare(LongLong left, object right)
{
if (right is LongLong)
return Compare(left, (LongLong) right);
if (right is bool)
return Compare(left, new LongLong((bool) right));
if (right is byte)
return Compare(left, new LongLong((byte) right));
if (right is char)
return Compare(left, new LongLong((char) right));
if (right is decimal)
return Compare(left, new LongLong((decimal) right));
if (right is double)
return Compare(left, new LongLong((double) right));
if (right is short)
return Compare(left, new LongLong((short) right));
if (right is int)
return Compare(left, new LongLong((int) right));
if (right is long)
return Compare(left, new LongLong((long) right));
if (right is sbyte)
return Compare(left, new LongLong((sbyte) right));
if (right is float)
return Compare(left, new LongLong((float) right));
if (right is ushort)
return Compare(left, new LongLong((ushort) right));
if (right is uint)
return Compare(left, new LongLong((uint) right));
if (right is ulong)
return Compare(left, new LongLong((ulong) right));
var bytes = right as byte[];
if (bytes != null && bytes.Length != 64)
return Compare(left, new LongLong(bytes));
if (right is Guid)
return Compare(left, new LongLong((Guid) right));
throw new ArgumentException();
}
public static int Compare(LongLong left, LongLong right)
{
return left.B64.CompareTo(right.B64);
}
public int CompareTo(LongLong value)
{
return Compare(this, value);
}
public static implicit operator LongLong(bool value)
{
return new LongLong(value);
}
public static implicit operator LongLong(byte value)
{
return new LongLong(value);
}
public static implicit operator LongLong(char value)
{
return new LongLong(value);
}
public static explicit operator LongLong(decimal value)
{
return new LongLong(value);
}
public static explicit operator LongLong(double value)
{
return new LongLong(value);
}
public static implicit operator LongLong(short value)
{
return new LongLong(value);
}
public static implicit operator LongLong(int value)
{
return new LongLong(value);
}
public static implicit operator LongLong(long value)
{
return new LongLong(value);
}
public static implicit operator LongLong(sbyte value)
{
return new LongLong(value);
}
public static explicit operator LongLong(float value)
{
return new LongLong(value);
}
public static implicit operator LongLong(ushort value)
{
return new LongLong(value);
}
public static implicit operator LongLong(uint value)
{
return new LongLong(value);
}
public static implicit operator LongLong(ulong value)
{
return new LongLong(value);
}
public static implicit operator LongLong(UInt128 value)
{
return new LongLong(value);
}
public static explicit operator bool(LongLong value)
{
return (byte) value.B64 != 0;
}
public static explicit operator byte(LongLong value)
{
return (byte) value.B64;
}
public static explicit operator char(LongLong value)
{
return (char) (ushort) value.B64;
}
public static explicit operator decimal(LongLong value)
{
return new decimal((int) (value.B64 & 0xFFFFFFFF), 0, 0, false, 0);
}
public static explicit operator double(LongLong value)
{
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!double.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var d))
throw new OverflowException();
return d;
}
public static explicit operator float(LongLong value)
{
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!float.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var f))
throw new OverflowException();
return f;
}
public static explicit operator short(LongLong value)
{
return (short) (int) value.B64;
}
public static explicit operator int(LongLong value)
{
return (int) value.B64;
}
public static explicit operator long(LongLong value)
{
return (long) value.B64;
}
public static explicit operator uint(LongLong value)
{
return (uint) value.B64;
}
public static explicit operator ushort(LongLong value)
{
return (ushort) value.B64;
}
public static explicit operator ulong(LongLong value)
{
return value.B64;
}
public static explicit operator BigInteger(LongLong value)
{
return new BigInteger(value.ToByteArray().Concat(new byte[] {0}).ToArray());
}
public static explicit operator UInt128(LongLong value)
{
return new UInt128(value.B64);
}
public static bool operator >(LongLong left, LongLong right)
{
return Compare(left, right) > 0;
}
public static bool operator <(LongLong left, LongLong right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(LongLong left, LongLong right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(LongLong left, LongLong right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(LongLong left, LongLong right)
{
return Compare(left, right) != 0;
}
public static bool operator ==(LongLong left, LongLong right)
{
return Compare(left, right) == 0;
}
public static LongLong operator +(LongLong value)
{
return value;
}
public static LongLong operator ~(LongLong value)
{
return -(value + One);
}
public static LongLong operator -(LongLong value)
{
return Negate(value);
}
public static LongLong operator ++(LongLong value)
{
return value + 1;
}
public static LongLong operator --(LongLong value)
{
return value - 1;
}
public static LongLong Negate(LongLong value)
{
return new LongLong(~value.B64) + 1;
}
public static LongLong operator +(LongLong left, LongLong right)
{
left.B64 += right.B64;
return left;
}
public static LongLong operator -(LongLong left, LongLong right)
{
return left + -right;
}
public static LongLong Add(LongLong left, LongLong right)
{
return left + right;
}
public static LongLong Subtract(LongLong left, LongLong right)
{
return left - right;
}
public static LongLong Divide(LongLong dividend, LongLong divisor)
{
return DivRem(dividend, divisor, out var integer);
}
public static LongLong DivRem(LongLong dividend, LongLong divisor, out LongLong remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quotient, out var rem);
remainder = new LongLong(rem);
return new LongLong(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 (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 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 LongLong Remainder(LongLong dividend, LongLong divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static LongLong Max(LongLong left, LongLong right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static LongLong Min(LongLong left, LongLong right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static int GetBitWidth(LongLong n)
{
LongLong bitWidth = 1;
var v = n;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
while (bitWidth % 8 != 0)
bitWidth++;
return (int) bitWidth;
}
public static LongLong operator %(LongLong dividend, LongLong divisor)
{
return Remainder(dividend, divisor);
}
public static LongLong operator /(LongLong dividend, LongLong divisor)
{
return Divide(dividend, divisor);
}
public ulong[] ToUIn64Array()
{
return new[] {B64};
}
public ulong[] ToUIn64Array(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 8;
if (al * 8 != value.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 4;
if (al * 4 != value.Length)
al++;
var arr = new uint[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array(ulong[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var arr = new uint[value.Length * 2];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public ulong[] ToUIn64Array(uint[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 2;
if (al * 2 != value.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array()
{
var uia = new uint[16];
var ula = ToUIn64Array();
Buffer.BlockCopy(ula, 0, uia, 0, 64);
return uia;
}
public byte[] ToByteArray()
{
var ba = new byte[64];
var ula = ToUIn64Array();
Buffer.BlockCopy(ula, 0, ba, 0, 64);
return ba;
}
public static LongLong Multiply(LongLong left, LongLong right)
{
var xInts = left.ToUIn32Array();
var yInts = right.ToUIn32Array();
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 LongLong(mulInts);
}
public static LongLong operator *(LongLong left, LongLong right)
{
return Multiply(left, right);
}
public static LongLong operator >>(LongLong value, int shift)
{
var values = value.ToUIn64Array();
var valueLength = sizeof(ulong) * 8;
var length = values.Length;
shift = shift % (length * valueLength);
var shiftOffset = shift / valueLength;
var bshift = shift % valueLength;
var shifted = new ulong[length];
for (var i = 0; i < length; i++)
{
var ishift = i - shiftOffset;
if (ishift < 0)
continue;
shifted[ishift] |= values[i] >> bshift;
if (bshift > 0 && i + 1 < length)
shifted[ishift] |= values[i + 1] << (valueLength - bshift);
}
return new LongLong(shifted[0]);
}
public static LongLong operator <<(LongLong value, int shift)
{
var values = value.ToUIn64Array();
var valueLength = sizeof(ulong) * 8;
var length = values.Length;
shift %= length * valueLength;
var shiftOffset = shift / valueLength;
var bshift = shift % valueLength;
var shifted = new ulong[length];
for (var i = 0; i < length; i++)
{
var ishift = i + shiftOffset;
if (ishift >= length)
continue;
shifted[ishift] |= values[i] << bshift;
if (bshift > 0 && i - 1 >= 0)
shifted[ishift] |= values[i - 1] >> (valueLength - bshift);
}
return new LongLong(shifted[0]);
}
public static LongLong operator |(LongLong left, LongLong 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)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu | yu;
}
return new LongLong(z);
}
public static LongLong operator ^(LongLong left, LongLong right)
{
var x = left.ToUIn32Array();
var y = right.ToUIn32Array();
var z = new uint[Math.Max(x.Length, y.Length)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu ^ yu;
}
return new LongLong(z);
}
public static LongLong operator &(LongLong left, LongLong 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)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu & yu;
}
return new LongLong(z);
}
private class LongLongConverter : 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 LongLong();
}
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);
}
}
}
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// Used to verify correct mathematical operations before extending to 128, 256, 512, 1024, and 2048 versions
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(LongLongConverter))]
[DebuggerDisplay("{DDisplay}")]
public struct LongLong : IComparable<LongLong>, IComparable, IEquatable<LongLong>, IConvertible, IFormattable
{
private ulong B64;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public static LongLong Zero = new LongLong(0);
public static LongLong Ten = new LongLong(10);
public static LongLong One = new LongLong(1);
public static LongLong MaxValue = GetMaxValue();
public static LongLong MinValue = GetMinValue();
private static LongLong GetMaxValue()
{
return new LongLong(ulong.MaxValue);
}
private static LongLong GetMinValue()
{
return new LongLong(0);
}
public LongLong(LongLong value)
{
B64 = value.B64;
}
public LongLong(string value)
{
if (!TryParse(value, out var result))
throw new Exception("TryParse Failed.");
B64 = result.B64;
}
public LongLong(byte value)
{
B64 = value;
}
public LongLong(bool value)
{
B64 = (ulong) (value ? 1 : 0);
}
public LongLong(char value)
{
B64 = value;
}
public LongLong(decimal value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
var bits = decimal.GetBits(value);
B64 = (uint) bits[0];
if (value < 0)
B64 = ~B64;
}
public LongLong(double value) : this((decimal) value)
{
}
public LongLong(float value) : this((decimal) value)
{
}
public LongLong(short value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(int value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(long value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(UInt128 value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = value.B64;
}
public LongLong(sbyte value)
{
if (value < 0)
throw new Exception("Value must be a positive.");
B64 = (ulong) value;
}
public LongLong(ushort value)
{
B64 = value;
}
public LongLong(uint value)
{
B64 = value;
}
public LongLong(ulong value)
{
B64 = value;
}
public LongLong(Guid value) : this(value.ToByteArray())
{
}
public LongLong(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
if (value.Length != 64)
throw new Exception("Values length must be 64 bytes.");
B64 = BitConverter.ToUInt64(value, 0);
}
//public LongLong(ulong b64)
//{
// B64 = b64;
//}
public LongLong(uint[] array)
{
if (array == null)
throw new Exception("Array cannot be null.");
var b64 = new byte[8];
if (array.Length > 0)
{
Array.Copy(BitConverter.GetBytes(array[0]), 0, b64, 0, 4);
if (array.Length > 1)
Array.Copy(BitConverter.GetBytes(array[1]), 0, b64, 4, 4);
}
B64 = BitConverter.ToUInt64(b64, 0);
}
public int BitWidth
{
get
{
LongLong bitWidth = 1;
var v = this;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
while (bitWidth % 8 != 0)
bitWidth++;
return (int) bitWidth;
}
}
public override int GetHashCode()
{
return B64.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public bool Equals(LongLong obj)
{
return B64 == obj.B64;
}
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.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)));
}
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)
{
var buf = ToByteArray();
var bi = new BigInteger(buf.Concat(new byte[] {0}).ToArray());
return bi.ToString();
}
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;
}
public ulong ToUInt64(IFormatProvider provider)
{
return (ulong) 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 LongLong Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static LongLong Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static LongLong Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static LongLong 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 LongLong result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out LongLong 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 LongLong result)
{
if (value.Length > 128)
throw new OverflowException();
result = Zero;
var pos = 0;
for (var i = value.Length - 1; i >= 0; i--)
{
var ch = value[i];
ulong bch;
if (ch >= '0' && ch <= '9')
bch = (ulong) (ch - '0');
else if (ch >= 'A' && ch <= 'F')
bch = (ulong) (ch - 'A' + 10);
else if (ch >= 'a' && ch <= 'f')
bch = (ulong) (ch - 'a' + 10);
else
return false;
if (pos < 64)
result.B64 |= bch << pos;
pos += 4;
}
return true;
}
private static bool TryParseNum(string value, out LongLong 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)
{
return Convert.ToUInt16(B64);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
return Convert.ToUInt32(B64);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
return B64;
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public static int Compare(LongLong left, object right)
{
if (right is LongLong)
return Compare(left, (LongLong) right);
if (right is bool)
return Compare(left, new LongLong((bool) right));
if (right is byte)
return Compare(left, new LongLong((byte) right));
if (right is char)
return Compare(left, new LongLong((char) right));
if (right is decimal)
return Compare(left, new LongLong((decimal) right));
if (right is double)
return Compare(left, new LongLong((double) right));
if (right is short)
return Compare(left, new LongLong((short) right));
if (right is int)
return Compare(left, new LongLong((int) right));
if (right is long)
return Compare(left, new LongLong((long) right));
if (right is sbyte)
return Compare(left, new LongLong((sbyte) right));
if (right is float)
return Compare(left, new LongLong((float) right));
if (right is ushort)
return Compare(left, new LongLong((ushort) right));
if (right is uint)
return Compare(left, new LongLong((uint) right));
if (right is ulong)
return Compare(left, new LongLong((ulong) right));
var bytes = right as byte[];
if (bytes != null && bytes.Length != 64)
return Compare(left, new LongLong(bytes));
if (right is Guid)
return Compare(left, new LongLong((Guid) right));
throw new ArgumentException();
}
public static int Compare(LongLong left, LongLong right)
{
return left.B64.CompareTo(right.B64);
}
public int CompareTo(LongLong value)
{
return Compare(this, value);
}
public static implicit operator LongLong(bool value)
{
return new LongLong(value);
}
public static implicit operator LongLong(byte value)
{
return new LongLong(value);
}
public static implicit operator LongLong(char value)
{
return new LongLong(value);
}
public static explicit operator LongLong(decimal value)
{
return new LongLong(value);
}
public static explicit operator LongLong(double value)
{
return new LongLong(value);
}
public static implicit operator LongLong(short value)
{
return new LongLong(value);
}
public static implicit operator LongLong(int value)
{
return new LongLong(value);
}
public static implicit operator LongLong(long value)
{
return new LongLong(value);
}
public static implicit operator LongLong(sbyte value)
{
return new LongLong(value);
}
public static explicit operator LongLong(float value)
{
return new LongLong(value);
}
public static implicit operator LongLong(ushort value)
{
return new LongLong(value);
}
public static implicit operator LongLong(uint value)
{
return new LongLong(value);
}
public static implicit operator LongLong(ulong value)
{
return new LongLong(value);
}
public static implicit operator LongLong(UInt128 value)
{
return new LongLong(value);
}
public static explicit operator bool(LongLong value)
{
return (byte) value.B64 != 0;
}
public static explicit operator byte(LongLong value)
{
return (byte) value.B64;
}
public static explicit operator char(LongLong value)
{
return (char) (ushort) value.B64;
}
public static explicit operator decimal(LongLong value)
{
return new decimal((int) (value.B64 & 0xFFFFFFFF), 0, 0, false, 0);
}
public static explicit operator double(LongLong value)
{
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!double.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var d))
throw new OverflowException();
return d;
}
public static explicit operator float(LongLong value)
{
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!float.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var f))
throw new OverflowException();
return f;
}
public static explicit operator short(LongLong value)
{
return (short) (int) value.B64;
}
public static explicit operator int(LongLong value)
{
return (int) value.B64;
}
public static explicit operator long(LongLong value)
{
return (long) value.B64;
}
public static explicit operator uint(LongLong value)
{
return (uint) value.B64;
}
public static explicit operator ushort(LongLong value)
{
return (ushort) value.B64;
}
public static explicit operator ulong(LongLong value)
{
return value.B64;
}
public static explicit operator BigInteger(LongLong value)
{
return new BigInteger(value.ToByteArray().Concat(new byte[] {0}).ToArray());
}
public static explicit operator UInt128(LongLong value)
{
return new UInt128(value.B64);
}
public static bool operator >(LongLong left, LongLong right)
{
return Compare(left, right) > 0;
}
public static bool operator <(LongLong left, LongLong right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(LongLong left, LongLong right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(LongLong left, LongLong right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(LongLong left, LongLong right)
{
return Compare(left, right) != 0;
}
public static bool operator ==(LongLong left, LongLong right)
{
return Compare(left, right) == 0;
}
public static LongLong operator +(LongLong value)
{
return value;
}
public static LongLong operator ~(LongLong value)
{
return -(value + One);
}
public static LongLong operator -(LongLong value)
{
return Negate(value);
}
public static LongLong operator ++(LongLong value)
{
return value + 1;
}
public static LongLong operator --(LongLong value)
{
return value - 1;
}
public static LongLong Negate(LongLong value)
{
return new LongLong(~value.B64) + 1;
}
public static LongLong operator +(LongLong left, LongLong right)
{
left.B64 += right.B64;
return left;
}
public static LongLong operator -(LongLong left, LongLong right)
{
return left + -right;
}
public static LongLong Add(LongLong left, LongLong right)
{
return left + right;
}
public static LongLong Subtract(LongLong left, LongLong right)
{
return left - right;
}
public static LongLong Divide(LongLong dividend, LongLong divisor)
{
return DivRem(dividend, divisor, out var integer);
}
public static LongLong DivRem(LongLong dividend, LongLong divisor, out LongLong remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quotient, out var rem);
remainder = new LongLong(rem);
return new LongLong(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 (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 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 LongLong Remainder(LongLong dividend, LongLong divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static LongLong Max(LongLong left, LongLong right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static LongLong Min(LongLong left, LongLong right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static int GetBitWidth(LongLong n)
{
LongLong bitWidth = 1;
var v = n;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
while (bitWidth % 8 != 0)
bitWidth++;
return (int) bitWidth;
}
public static LongLong operator %(LongLong dividend, LongLong divisor)
{
return Remainder(dividend, divisor);
}
public static LongLong operator /(LongLong dividend, LongLong divisor)
{
return Divide(dividend, divisor);
}
public ulong[] ToUIn64Array()
{
return new[] {B64};
}
public ulong[] ToUIn64Array(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 8;
if (al * 8 != value.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array(byte[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 4;
if (al * 4 != value.Length)
al++;
var arr = new uint[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array(ulong[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var arr = new uint[value.Length * 2];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public ulong[] ToUIn64Array(uint[] value)
{
if (value == null)
throw new Exception("Value cannot be null.");
var al = value.Length / 2;
if (al * 2 != value.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(value, 0, arr, 0, value.Length);
return arr;
}
public uint[] ToUIn32Array()
{
var uia = new uint[16];
var ula = ToUIn64Array();
Buffer.BlockCopy(ula, 0, uia, 0, 64);
return uia;
}
public byte[] ToByteArray()
{
var ba = new byte[64];
var ula = ToUIn64Array();
Buffer.BlockCopy(ula, 0, ba, 0, 64);
return ba;
}
public static LongLong Multiply(LongLong left, LongLong right)
{
var xInts = left.ToUIn32Array();
var yInts = right.ToUIn32Array();
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 LongLong(mulInts);
}
public static LongLong operator *(LongLong left, LongLong right)
{
return Multiply(left, right);
}
public static LongLong operator >>(LongLong value, int shift)
{
var values = value.ToUIn64Array();
var valueLength = sizeof(ulong) * 8;
var length = values.Length;
shift = shift % (length * valueLength);
var shiftOffset = shift / valueLength;
var bshift = shift % valueLength;
var shifted = new ulong[length];
for (var i = 0; i < length; i++)
{
var ishift = i - shiftOffset;
if (ishift < 0)
continue;
shifted[ishift] |= values[i] >> bshift;
if (bshift > 0 && i + 1 < length)
shifted[ishift] |= values[i + 1] << (valueLength - bshift);
}
return new LongLong(shifted[0]);
}
public static LongLong operator <<(LongLong value, int shift)
{
var values = value.ToUIn64Array();
var valueLength = sizeof(ulong) * 8;
var length = values.Length;
shift %= length * valueLength;
var shiftOffset = shift / valueLength;
var bshift = shift % valueLength;
var shifted = new ulong[length];
for (var i = 0; i < length; i++)
{
var ishift = i + shiftOffset;
if (ishift >= length)
continue;
shifted[ishift] |= values[i] << bshift;
if (bshift > 0 && i - 1 >= 0)
shifted[ishift] |= values[i - 1] >> (valueLength - bshift);
}
return new LongLong(shifted[0]);
}
public static LongLong operator |(LongLong left, LongLong 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)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu | yu;
}
return new LongLong(z);
}
public static LongLong operator ^(LongLong left, LongLong right)
{
var x = left.ToUIn32Array();
var y = right.ToUIn32Array();
var z = new uint[Math.Max(x.Length, y.Length)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu ^ yu;
}
return new LongLong(z);
}
public static LongLong operator &(LongLong left, LongLong 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)];
for (var i = 0; i < z.Length; i++)
{
var xu = i < x.Length ? x[i] : 0u;
var yu = i < y.Length ? y[i] : 0u;
z[i] = xu & yu;
}
return new LongLong(z);
}
private class LongLongConverter : 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 LongLong();
}
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);
}
}
}
using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; using System.Text; /// <summary> /// Used to verify correct mathematical operations before extending to 128, 256, 512, 1024, and 2048 versions /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(LongLongConverter))] [DebuggerDisplay("{DDisplay}")] public struct LongLong : IComparable<LongLong>, IComparable, IEquatable<LongLong>, IConvertible, IFormattable { private ulong B64; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public static LongLong Zero = new LongLong(0); public static LongLong Ten = new LongLong(10); public static LongLong One = new LongLong(1); public static LongLong MaxValue = GetMaxValue(); public static LongLong MinValue = GetMinValue(); private static LongLong GetMaxValue() { return new LongLong(ulong.MaxValue); } private static LongLong GetMinValue() { return new LongLong(0); } public LongLong(LongLong value) { B64 = value.B64; } public LongLong(string value) { if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); B64 = result.B64; } public LongLong(byte value) { B64 = value; } public LongLong(bool value) { B64 = (ulong) (value ? 1 : 0); } public LongLong(char value) { B64 = value; } public LongLong(decimal value) { if (value < 0) throw new Exception("Value must be a positive."); var bits = decimal.GetBits(value); B64 = (uint) bits[0]; if (value < 0) B64 = ~B64; } public LongLong(double value) : this((decimal) value) { } public LongLong(float value) : this((decimal) value) { } public LongLong(short value) { if (value < 0) throw new Exception("Value must be a positive."); B64 = (ulong) value; } public LongLong(int value) { if (value < 0) throw new Exception("Value must be a positive."); B64 = (ulong) value; } public LongLong(long value) { if (value < 0) throw new Exception("Value must be a positive."); B64 = (ulong) value; } public LongLong(UInt128 value) { if (value < 0) throw new Exception("Value must be a positive."); B64 = value.B64; } public LongLong(sbyte value) { if (value < 0) throw new Exception("Value must be a positive."); B64 = (ulong) value; } public LongLong(ushort value) { B64 = value; } public LongLong(uint value) { B64 = value; } public LongLong(ulong value) { B64 = value; } public LongLong(Guid value) : this(value.ToByteArray()) { } public LongLong(byte[] value) { if (value == null) throw new Exception("Value cannot be null."); if (value.Length != 64) throw new Exception("Values length must be 64 bytes."); B64 = BitConverter.ToUInt64(value, 0); } //public LongLong(ulong b64) //{ // B64 = b64; //} public LongLong(uint[] array) { if (array == null) throw new Exception("Array cannot be null."); var b64 = new byte[8]; if (array.Length > 0) { Array.Copy(BitConverter.GetBytes(array[0]), 0, b64, 0, 4); if (array.Length > 1) Array.Copy(BitConverter.GetBytes(array[1]), 0, b64, 4, 4); } B64 = BitConverter.ToUInt64(b64, 0); } public int BitWidth { get { LongLong bitWidth = 1; var v = this; while ((v >>= 1) > 0) bitWidth++; if (bitWidth < 8) bitWidth = 8; while (bitWidth % 8 != 0) bitWidth++; return (int) bitWidth; } } public override int GetHashCode() { return B64.GetHashCode(); } public override bool Equals(object obj) { return base.Equals(obj); } public bool Equals(LongLong obj) { return B64 == obj.B64; } 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.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))); } 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) { var buf = ToByteArray(); var bi = new BigInteger(buf.Concat(new byte[] {0}).ToArray()); return bi.ToString(); } 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; } public ulong ToUInt64(IFormatProvider provider) { return (ulong) 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 LongLong Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static LongLong Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static LongLong Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static LongLong 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 LongLong result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out LongLong 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 LongLong result) { if (value.Length > 128) throw new OverflowException(); result = Zero; var pos = 0; for (var i = value.Length - 1; i >= 0; i--) { var ch = value[i]; ulong bch; if (ch >= '0' && ch <= '9') bch = (ulong) (ch - '0'); else if (ch >= 'A' && ch <= 'F') bch = (ulong) (ch - 'A' + 10); else if (ch >= 'a' && ch <= 'f') bch = (ulong) (ch - 'a' + 10); else return false; if (pos < 64) result.B64 |= bch << pos; pos += 4; } return true; } private static bool TryParseNum(string value, out LongLong 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) { return Convert.ToUInt16(B64); } uint IConvertible.ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(B64); } ulong IConvertible.ToUInt64(IFormatProvider provider) { return B64; } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public static int Compare(LongLong left, object right) { if (right is LongLong) return Compare(left, (LongLong) right); if (right is bool) return Compare(left, new LongLong((bool) right)); if (right is byte) return Compare(left, new LongLong((byte) right)); if (right is char) return Compare(left, new LongLong((char) right)); if (right is decimal) return Compare(left, new LongLong((decimal) right)); if (right is double) return Compare(left, new LongLong((double) right)); if (right is short) return Compare(left, new LongLong((short) right)); if (right is int) return Compare(left, new LongLong((int) right)); if (right is long) return Compare(left, new LongLong((long) right)); if (right is sbyte) return Compare(left, new LongLong((sbyte) right)); if (right is float) return Compare(left, new LongLong((float) right)); if (right is ushort) return Compare(left, new LongLong((ushort) right)); if (right is uint) return Compare(left, new LongLong((uint) right)); if (right is ulong) return Compare(left, new LongLong((ulong) right)); var bytes = right as byte[]; if (bytes != null && bytes.Length != 64) return Compare(left, new LongLong(bytes)); if (right is Guid) return Compare(left, new LongLong((Guid) right)); throw new ArgumentException(); } public static int Compare(LongLong left, LongLong right) { return left.B64.CompareTo(right.B64); } public int CompareTo(LongLong value) { return Compare(this, value); } public static implicit operator LongLong(bool value) { return new LongLong(value); } public static implicit operator LongLong(byte value) { return new LongLong(value); } public static implicit operator LongLong(char value) { return new LongLong(value); } public static explicit operator LongLong(decimal value) { return new LongLong(value); } public static explicit operator LongLong(double value) { return new LongLong(value); } public static implicit operator LongLong(short value) { return new LongLong(value); } public static implicit operator LongLong(int value) { return new LongLong(value); } public static implicit operator LongLong(long value) { return new LongLong(value); } public static implicit operator LongLong(sbyte value) { return new LongLong(value); } public static explicit operator LongLong(float value) { return new LongLong(value); } public static implicit operator LongLong(ushort value) { return new LongLong(value); } public static implicit operator LongLong(uint value) { return new LongLong(value); } public static implicit operator LongLong(ulong value) { return new LongLong(value); } public static implicit operator LongLong(UInt128 value) { return new LongLong(value); } public static explicit operator bool(LongLong value) { return (byte) value.B64 != 0; } public static explicit operator byte(LongLong value) { return (byte) value.B64; } public static explicit operator char(LongLong value) { return (char) (ushort) value.B64; } public static explicit operator decimal(LongLong value) { return new decimal((int) (value.B64 & 0xFFFFFFFF), 0, 0, false, 0); } public static explicit operator double(LongLong value) { var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!double.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var d)) throw new OverflowException(); return d; } public static explicit operator float(LongLong value) { var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!float.TryParse(value.ToString(nfi), NumberStyles.Number, nfi, out var f)) throw new OverflowException(); return f; } public static explicit operator short(LongLong value) { return (short) (int) value.B64; } public static explicit operator int(LongLong value) { return (int) value.B64; } public static explicit operator long(LongLong value) { return (long) value.B64; } public static explicit operator uint(LongLong value) { return (uint) value.B64; } public static explicit operator ushort(LongLong value) { return (ushort) value.B64; } public static explicit operator ulong(LongLong value) { return value.B64; } public static explicit operator BigInteger(LongLong value) { return new BigInteger(value.ToByteArray().Concat(new byte[] {0}).ToArray()); } public static explicit operator UInt128(LongLong value) { return new UInt128(value.B64); } public static bool operator >(LongLong left, LongLong right) { return Compare(left, right) > 0; } public static bool operator <(LongLong left, LongLong right) { return Compare(left, right) < 0; } public static bool operator >=(LongLong left, LongLong right) { return Compare(left, right) >= 0; } public static bool operator <=(LongLong left, LongLong right) { return Compare(left, right) <= 0; } public static bool operator !=(LongLong left, LongLong right) { return Compare(left, right) != 0; } public static bool operator ==(LongLong left, LongLong right) { return Compare(left, right) == 0; } public static LongLong operator +(LongLong value) { return value; } public static LongLong operator ~(LongLong value) { return -(value + One); } public static LongLong operator -(LongLong value) { return Negate(value); } public static LongLong operator ++(LongLong value) { return value + 1; } public static LongLong operator --(LongLong value) { return value - 1; } public static LongLong Negate(LongLong value) { return new LongLong(~value.B64) + 1; } public static LongLong operator +(LongLong left, LongLong right) { left.B64 += right.B64; return left; } public static LongLong operator -(LongLong left, LongLong right) { return left + -right; } public static LongLong Add(LongLong left, LongLong right) { return left + right; } public static LongLong Subtract(LongLong left, LongLong right) { return left - right; } public static LongLong Divide(LongLong dividend, LongLong divisor) { return DivRem(dividend, divisor, out var integer); } public static LongLong DivRem(LongLong dividend, LongLong divisor, out LongLong remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.ToUIn32Array(), divisor.ToUIn32Array(), out var quotient, out var rem); remainder = new LongLong(rem); return new LongLong(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 (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 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 LongLong Remainder(LongLong dividend, LongLong divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static LongLong Max(LongLong left, LongLong right) { return left.CompareTo(right) < 0 ? right : left; } public static LongLong Min(LongLong left, LongLong right) { return left.CompareTo(right) <= 0 ? left : right; } public static int GetBitWidth(LongLong n) { LongLong bitWidth = 1; var v = n; while ((v >>= 1) > 0) bitWidth++; if (bitWidth < 8) bitWidth = 8; while (bitWidth % 8 != 0) bitWidth++; return (int) bitWidth; } public static LongLong operator %(LongLong dividend, LongLong divisor) { return Remainder(dividend, divisor); } public static LongLong operator /(LongLong dividend, LongLong divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { return new[] {B64}; } public ulong[] ToUIn64Array(byte[] value) { if (value == null) throw new Exception("Value cannot be null."); var al = value.Length / 8; if (al * 8 != value.Length) al++; var arr = new ulong[al]; Buffer.BlockCopy(value, 0, arr, 0, value.Length); return arr; } public uint[] ToUIn32Array(byte[] value) { if (value == null) throw new Exception("Value cannot be null."); var al = value.Length / 4; if (al * 4 != value.Length) al++; var arr = new uint[al]; Buffer.BlockCopy(value, 0, arr, 0, value.Length); return arr; } public uint[] ToUIn32Array(ulong[] value) { if (value == null) throw new Exception("Value cannot be null."); var arr = new uint[value.Length * 2]; Buffer.BlockCopy(value, 0, arr, 0, value.Length); return arr; } public ulong[] ToUIn64Array(uint[] value) { if (value == null) throw new Exception("Value cannot be null."); var al = value.Length / 2; if (al * 2 != value.Length) al++; var arr = new ulong[al]; Buffer.BlockCopy(value, 0, arr, 0, value.Length); return arr; } public uint[] ToUIn32Array() { var uia = new uint[16]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, uia, 0, 64); return uia; } public byte[] ToByteArray() { var ba = new byte[64]; var ula = ToUIn64Array(); Buffer.BlockCopy(ula, 0, ba, 0, 64); return ba; } public static LongLong Multiply(LongLong left, LongLong right) { var xInts = left.ToUIn32Array(); var yInts = right.ToUIn32Array(); 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 LongLong(mulInts); } public static LongLong operator *(LongLong left, LongLong right) { return Multiply(left, right); } public static LongLong operator >>(LongLong value, int shift) { var values = value.ToUIn64Array(); var valueLength = sizeof(ulong) * 8; var length = values.Length; shift = shift % (length * valueLength); var shiftOffset = shift / valueLength; var bshift = shift % valueLength; var shifted = new ulong[length]; for (var i = 0; i < length; i++) { var ishift = i - shiftOffset; if (ishift < 0) continue; shifted[ishift] |= values[i] >> bshift; if (bshift > 0 && i + 1 < length) shifted[ishift] |= values[i + 1] << (valueLength - bshift); } return new LongLong(shifted[0]); } public static LongLong operator <<(LongLong value, int shift) { var values = value.ToUIn64Array(); var valueLength = sizeof(ulong) * 8; var length = values.Length; shift %= length * valueLength; var shiftOffset = shift / valueLength; var bshift = shift % valueLength; var shifted = new ulong[length]; for (var i = 0; i < length; i++) { var ishift = i + shiftOffset; if (ishift >= length) continue; shifted[ishift] |= values[i] << bshift; if (bshift > 0 && i - 1 >= 0) shifted[ishift] |= values[i - 1] >> (valueLength - bshift); } return new LongLong(shifted[0]); } public static LongLong operator |(LongLong left, LongLong 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)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu | yu; } return new LongLong(z); } public static LongLong operator ^(LongLong left, LongLong right) { var x = left.ToUIn32Array(); var y = right.ToUIn32Array(); var z = new uint[Math.Max(x.Length, y.Length)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu ^ yu; } return new LongLong(z); } public static LongLong operator &(LongLong left, LongLong 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)]; for (var i = 0; i < z.Length; i++) { var xu = i < x.Length ? x[i] : 0u; var yu = i < y.Length ? y[i] : 0u; z[i] = xu & yu; } return new LongLong(z); } private class LongLongConverter : 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 LongLong(); } 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); } } }