BigIntegerHelper.cs

BigInteger Helper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
public static class BigIntegerHelper
{
private static readonly BigInteger Ten = new BigInteger(10);
private static readonly BigInteger Three = new BigInteger(3);
private static readonly BigInteger ONE = new BigInteger(1);
private static readonly BigInteger Two = new BigInteger(2);
public static BigInteger BigIntegerBase2(this string binaryValue)
{
BigInteger res = 0;
if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length) return res;
foreach (var c in binaryValue)
{
res <<= 1;
res += c == '1' ? 1 : 0;
}
return res;
}
public static BigInteger BigIntegerBase16(this string hexNumber)
{
if (string.IsNullOrEmpty(hexNumber))
throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
hexNumber = hexNumber.ToUpper();
if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
hexNumber = hexNumber.Substring(2);
var bytes = Enumerable.Range(0, hexNumber.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
.ToArray();
return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
}
public static BigInteger BigIntegerBase10(this string str)
{
var number = new BigInteger();
int i;
for (i = 0; i < str.Length; i++)
if (str[i] >= '0' && str[i] <= '9')
number = number * Ten + long.Parse(str[i].ToString());
return number;
}
private static byte[] HexToByteArray(this string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static BigInteger ToBigInteger(this char ul)
{
return new BigInteger(ul);
}
public static BigInteger ToBigInteger(this byte ul)
{
return new BigInteger(ul);
}
public static BigInteger ToBigInteger(this sbyte ul)
{
return new BigInteger(ul);
}
public static BigInteger ToBigInteger(this short ul)
{
return new BigInteger(ul);
}
public static BigInteger ToBigInteger(this ushort ul)
{
return new BigInteger(ul);
}
public static BigInteger ToBigInteger(this int ul)
{
return new BigInteger((ulong) ul);
}
public static BigInteger ToBigInteger(this uint ul)
{
return new BigInteger((ulong) ul);
}
public static BigInteger ToBigInteger(this long ul)
{
return new BigInteger((ulong) ul);
}
public static BigInteger ToBigInteger(this ulong ul)
{
return new BigInteger(ul);
}
public static BigInteger ModPow(this BigInteger n, BigInteger e, BigInteger m)
{
var s = ONE;
var u = e;
while (!u.IsZero)
{
if ((u & ONE) == 1)
s = s * n % m;
u >>= 1;
n = n * n % m;
}
return s;
}
public static double ConfidenceToProbability(int confidence)
{
var a = Two.Pow(confidence);
var b = 100.0 / (double) a;
var c = 100.0 - b;
return c;
}
public static BigInteger Pow(this BigInteger n, BigInteger exp)
{
var y = ONE;
var z = n;
while (exp != 0)
{
if ((exp & 0x1) == 1)
y *= z;
exp >>= 1;
if (exp != 0)
z *= z;
}
return y;
}
public static List<BigInteger> GetFactors(this BigInteger n)
{
var Factors = new List<BigInteger>();
var s = n.Sqrt();
var a = Three;
while (a < s)
{
if (n % a == 0)
{
Factors.Add(a);
if (a * a != n)
Factors.Add(n / a);
}
a += 2;
}
return Factors;
}
public static BigInteger Gcd(this BigInteger a, BigInteger b)
{
while (b > BigInteger.Zero)
{
var r = a % b;
a = b;
b = r;
}
return a;
}
public static BigInteger Lcm(this BigInteger a, BigInteger b)
{
return a * b / a.Gcd(b);
}
public static BigInteger BigIntegerBase2(this BigInteger bi, string binaryValue)
{
bi = BigInteger.Zero;
if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length)
return bi;
foreach (var c in binaryValue)
{
bi <<= 1;
bi += c == '1' ? 1 : 0;
}
return bi;
}
public static int GetByteWidth(this BigInteger n)
{
return GetBitWidth(n) >> 3;
}
public static int GetBitWidth(this BigInteger n)
{
BigInteger bitWidth = 1;
var v = n;
while ((v >>= 1) > 0)
bitWidth++;
if (bitWidth < 8)
bitWidth = 8;
return (int) bitWidth;
}
public static BigInteger GetMaxValue(this BigInteger bi, int bitLength)
{
if (bi.Sign == -1)
bitLength -= 1;
return BigInteger.One << bitLength;
}
public static BigInteger GetMaxValue(this BigInteger bi)
{
var bitLength = bi.GetBitWidth();
if (bi.Sign == -1)
bitLength -= 1;
return (BigInteger.One << bitLength) - BigInteger.One;
}
public static BigInteger GetMaxValueBitWidth(int bitLength)
{
return (BigInteger.One << bitLength) - BigInteger.One;
}
public static BigInteger BigIntegerBase16(this BigInteger bi, string hexNumber)
{
if (string.IsNullOrEmpty(hexNumber))
throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
hexNumber = hexNumber.ToUpper();
if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
hexNumber = hexNumber.Substring(2);
var bytes = Enumerable.Range(0, hexNumber.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
.ToArray();
return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
}
public static BigInteger BigIntegerBase10(this BigInteger bi, string str)
{
if (str[0] == '-' || str.IndexOf('.') != -1)
throw new Exception($"Invalid numeric string. Only positive numbers and whole numbers are allowed. Value={str}");
var number = new BigInteger();
int i;
for (i = 0; i < str.Length; i++)
if (str[i] >= '0' && str[i] <= '9')
number = number * Ten + long.Parse(str[i].ToString());
return number;
}
public static string ToBinaryString(this BigInteger bigint)
{
var bytes = bigint.ToByteArray();
var index = bytes.Length - 1;
var base2 = new StringBuilder(bytes.Length * 8);
var binary = Convert.ToString(bytes[index], 2);
if (binary[0] != '0' && bigint.Sign == 1) base2.Append('0');
base2.Append(binary);
for (index--; index >= 0; index--)
base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
return base2.ToString();
}
public static string ToHexString(this BigInteger bi)
{
var bytes = bi.ToByteArray();
var sb = new StringBuilder();
foreach (var b in bytes)
{
var hex = b.ToString("X2");
sb.Append(hex);
}
return sb.ToString();
}
public static string ToOctalString(this BigInteger bigint)
{
var bytes = bigint.ToByteArray();
var index = bytes.Length - 1;
var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8);
var rem = bytes.Length % 3;
if (rem == 0) rem = 3;
var base0 = 0;
while (rem != 0)
{
base0 <<= 8;
base0 += bytes[index--];
rem--;
}
var octal = Convert.ToString(base0, 8);
if (octal[0] != '0' && bigint.Sign == 1) base8.Append('0');
base8.Append(octal);
while (index >= 0)
{
base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
index -= 3;
}
return base8.ToString();
}
/// <summary>
/// Approximation
/// </summary>
public static BigInteger Sqrt(this BigInteger n)
{
var q = BigInteger.One << ((int) BigInteger.Log(n, 2) >> 1);
var m = BigInteger.Zero;
while (BigInteger.Abs(q - m) >= 1)
{
m = q;
q = (m + n / m) >> 1;
}
return q;
}
}
using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; public static class BigIntegerHelper { private static readonly BigInteger Ten = new BigInteger(10); private static readonly BigInteger Three = new BigInteger(3); private static readonly BigInteger ONE = new BigInteger(1); private static readonly BigInteger Two = new BigInteger(2); public static BigInteger BigIntegerBase2(this string binaryValue) { BigInteger res = 0; if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length) return res; foreach (var c in binaryValue) { res <<= 1; res += c == '1' ? 1 : 0; } return res; } public static BigInteger BigIntegerBase16(this string hexNumber) { if (string.IsNullOrEmpty(hexNumber)) throw new Exception("Error: hexNumber cannot be either null or have a length of zero."); if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX")) throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX"); hexNumber = hexNumber.ToUpper(); if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1) hexNumber = hexNumber.Substring(2); var bytes = Enumerable.Range(0, hexNumber.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16)) .ToArray(); return new BigInteger(bytes.Concat(new byte[] {0}).ToArray()); } public static BigInteger BigIntegerBase10(this string str) { var number = new BigInteger(); int i; for (i = 0; i < str.Length; i++) if (str[i] >= '0' && str[i] <= '9') number = number * Ten + long.Parse(str[i].ToString()); return number; } private static byte[] HexToByteArray(this string hex) { return Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); } public static BigInteger ToBigInteger(this char ul) { return new BigInteger(ul); } public static BigInteger ToBigInteger(this byte ul) { return new BigInteger(ul); } public static BigInteger ToBigInteger(this sbyte ul) { return new BigInteger(ul); } public static BigInteger ToBigInteger(this short ul) { return new BigInteger(ul); } public static BigInteger ToBigInteger(this ushort ul) { return new BigInteger(ul); } public static BigInteger ToBigInteger(this int ul) { return new BigInteger((ulong) ul); } public static BigInteger ToBigInteger(this uint ul) { return new BigInteger((ulong) ul); } public static BigInteger ToBigInteger(this long ul) { return new BigInteger((ulong) ul); } public static BigInteger ToBigInteger(this ulong ul) { return new BigInteger(ul); } public static BigInteger ModPow(this BigInteger n, BigInteger e, BigInteger m) { var s = ONE; var u = e; while (!u.IsZero) { if ((u & ONE) == 1) s = s * n % m; u >>= 1; n = n * n % m; } return s; } public static double ConfidenceToProbability(int confidence) { var a = Two.Pow(confidence); var b = 100.0 / (double) a; var c = 100.0 - b; return c; } public static BigInteger Pow(this BigInteger n, BigInteger exp) { var y = ONE; var z = n; while (exp != 0) { if ((exp & 0x1) == 1) y *= z; exp >>= 1; if (exp != 0) z *= z; } return y; } public static List<BigInteger> GetFactors(this BigInteger n) { var Factors = new List<BigInteger>(); var s = n.Sqrt(); var a = Three; while (a < s) { if (n % a == 0) { Factors.Add(a); if (a * a != n) Factors.Add(n / a); } a += 2; } return Factors; } public static BigInteger Gcd(this BigInteger a, BigInteger b) { while (b > BigInteger.Zero) { var r = a % b; a = b; b = r; } return a; } public static BigInteger Lcm(this BigInteger a, BigInteger b) { return a * b / a.Gcd(b); } public static BigInteger BigIntegerBase2(this BigInteger bi, string binaryValue) { bi = BigInteger.Zero; if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length) return bi; foreach (var c in binaryValue) { bi <<= 1; bi += c == '1' ? 1 : 0; } return bi; } public static int GetByteWidth(this BigInteger n) { return GetBitWidth(n) >> 3; } public static int GetBitWidth(this BigInteger n) { BigInteger bitWidth = 1; var v = n; while ((v >>= 1) > 0) bitWidth++; if (bitWidth < 8) bitWidth = 8; return (int) bitWidth; } public static BigInteger GetMaxValue(this BigInteger bi, int bitLength) { if (bi.Sign == -1) bitLength -= 1; return BigInteger.One << bitLength; } public static BigInteger GetMaxValue(this BigInteger bi) { var bitLength = bi.GetBitWidth(); if (bi.Sign == -1) bitLength -= 1; return (BigInteger.One << bitLength) - BigInteger.One; } public static BigInteger GetMaxValueBitWidth(int bitLength) { return (BigInteger.One << bitLength) - BigInteger.One; } public static BigInteger BigIntegerBase16(this BigInteger bi, string hexNumber) { if (string.IsNullOrEmpty(hexNumber)) throw new Exception("Error: hexNumber cannot be either null or have a length of zero."); if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX")) throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX"); hexNumber = hexNumber.ToUpper(); if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1) hexNumber = hexNumber.Substring(2); var bytes = Enumerable.Range(0, hexNumber.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16)) .ToArray(); return new BigInteger(bytes.Concat(new byte[] {0}).ToArray()); } public static BigInteger BigIntegerBase10(this BigInteger bi, string str) { if (str[0] == '-' || str.IndexOf('.') != -1) throw new Exception($"Invalid numeric string. Only positive numbers and whole numbers are allowed. Value={str}"); var number = new BigInteger(); int i; for (i = 0; i < str.Length; i++) if (str[i] >= '0' && str[i] <= '9') number = number * Ten + long.Parse(str[i].ToString()); return number; } public static string ToBinaryString(this BigInteger bigint) { var bytes = bigint.ToByteArray(); var index = bytes.Length - 1; var base2 = new StringBuilder(bytes.Length * 8); var binary = Convert.ToString(bytes[index], 2); if (binary[0] != '0' && bigint.Sign == 1) base2.Append('0'); base2.Append(binary); for (index--; index >= 0; index--) base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0')); return base2.ToString(); } public static string ToHexString(this BigInteger bi) { var bytes = bi.ToByteArray(); var sb = new StringBuilder(); foreach (var b in bytes) { var hex = b.ToString("X2"); sb.Append(hex); } return sb.ToString(); } public static string ToOctalString(this BigInteger bigint) { var bytes = bigint.ToByteArray(); var index = bytes.Length - 1; var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8); var rem = bytes.Length % 3; if (rem == 0) rem = 3; var base0 = 0; while (rem != 0) { base0 <<= 8; base0 += bytes[index--]; rem--; } var octal = Convert.ToString(base0, 8); if (octal[0] != '0' && bigint.Sign == 1) base8.Append('0'); base8.Append(octal); while (index >= 0) { base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2]; base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0')); index -= 3; } return base8.ToString(); } /// <summary> /// Approximation /// </summary> public static BigInteger Sqrt(this BigInteger n) { var q = BigInteger.One << ((int) BigInteger.Log(n, 2) >> 1); var m = BigInteger.Zero; while (BigInteger.Abs(q - m) >= 1) { m = q; q = (m + n / m) >> 1; } return q; } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
public static class BigIntegerHelper
{
    private static readonly BigInteger Ten   = new BigInteger(10);
    private static readonly BigInteger Three = new BigInteger(3);
    private static readonly BigInteger ONE   = new BigInteger(1);
    private static readonly BigInteger Two   = new BigInteger(2);
    public static BigInteger BigIntegerBase2(this string binaryValue)
    {
        BigInteger res = 0;
        if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length) return res;
        foreach (var c in binaryValue)
        {
            res <<= 1;
            res +=  c == '1' ? 1 : 0;
        }
        return res;
    }
    public static BigInteger BigIntegerBase16(this string hexNumber)
    {
        if (string.IsNullOrEmpty(hexNumber))
            throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
        if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
            throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
        hexNumber = hexNumber.ToUpper();
        if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
            hexNumber = hexNumber.Substring(2);
        var bytes = Enumerable.Range(0, hexNumber.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
            .ToArray();
        return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
    }
    public static BigInteger BigIntegerBase10(this string str)
    {
        var number = new BigInteger();
        int i;
        for (i = 0; i < str.Length; i++)
            if (str[i] >= '0' && str[i] <= '9')
                number = number * Ten + long.Parse(str[i].ToString());
        return number;
    }
    private static byte[] HexToByteArray(this string hex)
    {
        return Enumerable.Range(0, hex.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
            .ToArray();
    }
    public static BigInteger ToBigInteger(this char ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this byte ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this sbyte ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this short ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this ushort ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ToBigInteger(this int ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this uint ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this long ul)
    {
        return new BigInteger((ulong) ul);
    }
    public static BigInteger ToBigInteger(this ulong ul)
    {
        return new BigInteger(ul);
    }
    public static BigInteger ModPow(this BigInteger n, BigInteger e, BigInteger m)
    {
        var s = ONE;
        var u = e;
        while (!u.IsZero)
        {
            if ((u & ONE) == 1)
                s = s * n % m;
            u >>= 1;
            n =   n * n % m;
        }
        return s;
    }
    public static double ConfidenceToProbability(int confidence)
    {
        var a = Two.Pow(confidence);
        var b = 100.0 / (double) a;
        var c = 100.0 - b;
        return c;
    }
    public static BigInteger Pow(this BigInteger n, BigInteger exp)
    {
        var y = ONE;
        var z = n;
        while (exp != 0)
        {
            if ((exp & 0x1) == 1)
                y *= z;
            exp >>= 1;
            if (exp != 0)
                z *= z;
        }
        return y;
    }
    public static List<BigInteger> GetFactors(this BigInteger n)
    {
        var Factors = new List<BigInteger>();
        var s       = n.Sqrt();
        var a       = Three;
        while (a < s)
        {
            if (n % a == 0)
            {
                Factors.Add(a);
                if (a * a != n)
                    Factors.Add(n / a);
            }
            a += 2;
        }
        return Factors;
    }
    public static BigInteger Gcd(this BigInteger a, BigInteger b)
    {
        while (b > BigInteger.Zero)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static BigInteger Lcm(this BigInteger a, BigInteger b)
    {
        return a * b / a.Gcd(b);
    }
    public static BigInteger BigIntegerBase2(this BigInteger bi, string binaryValue)
    {
        bi = BigInteger.Zero;
        if (binaryValue.Count(b => b == '1') + binaryValue.Count(b => b == '0') != binaryValue.Length)
            return bi;
        foreach (var c in binaryValue)
        {
            bi <<= 1;
            bi +=  c == '1' ? 1 : 0;
        }
        return bi;
    }
    public static int GetByteWidth(this BigInteger n)
    {
        return GetBitWidth(n) >> 3;
    }
    public static int GetBitWidth(this BigInteger n)
    {
        BigInteger bitWidth = 1;
        var        v        = n;
        while ((v >>= 1) > 0)
            bitWidth++;
        if (bitWidth < 8)
            bitWidth = 8;
        return (int) bitWidth;
    }
    public static BigInteger GetMaxValue(this BigInteger bi, int bitLength)
    {
        if (bi.Sign == -1)
            bitLength -= 1;
        return BigInteger.One << bitLength;
    }
    public static BigInteger GetMaxValue(this BigInteger bi)
    {
        var bitLength = bi.GetBitWidth();
        if (bi.Sign == -1)
            bitLength -= 1;
        return (BigInteger.One << bitLength) - BigInteger.One;
    }
    public static BigInteger GetMaxValueBitWidth(int bitLength)
    {
        return (BigInteger.One << bitLength) - BigInteger.One;
    }
    public static BigInteger BigIntegerBase16(this BigInteger bi, string hexNumber)
    {
        if (string.IsNullOrEmpty(hexNumber))
            throw new Exception("Error: hexNumber cannot be either null or have a length of zero.");
        if (!hexNumber.ContainsOnly("0123456789abcdefABCDEFxX"))
            throw new Exception("Error: hexNumber cannot contain characters other than 0-9,a-f,A-F, or xX");
        hexNumber = hexNumber.ToUpper();
        if (hexNumber.IndexOf("0X", StringComparison.OrdinalIgnoreCase) != -1)
            hexNumber = hexNumber.Substring(2);
        var bytes = Enumerable.Range(0, hexNumber.Length)
            .Where(x => x % 2 == 0)
            .Select(x => Convert.ToByte(hexNumber.Substring(x, 2), 16))
            .ToArray();
        return new BigInteger(bytes.Concat(new byte[] {0}).ToArray());
    }
    public static BigInteger BigIntegerBase10(this BigInteger bi, string str)
    {
        if (str[0] == '-' || str.IndexOf('.') != -1)
            throw new Exception($"Invalid numeric string. Only positive numbers and whole numbers are allowed. Value={str}");
        var number = new BigInteger();
        int i;
        for (i = 0; i < str.Length; i++)
            if (str[i] >= '0' && str[i] <= '9')
                number = number * Ten + long.Parse(str[i].ToString());
        return number;
    }
    public static string ToBinaryString(this BigInteger bigint)
    {
        var bytes  = bigint.ToByteArray();
        var index  = bytes.Length - 1;
        var base2  = new StringBuilder(bytes.Length * 8);
        var binary = Convert.ToString(bytes[index], 2);
        if (binary[0] != '0' && bigint.Sign == 1) base2.Append('0');
        base2.Append(binary);
        for (index--; index >= 0; index--)
            base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
        return base2.ToString();
    }
    public static string ToHexString(this BigInteger bi)
    {
        var bytes = bi.ToByteArray();
        var sb    = new StringBuilder();
        foreach (var b in bytes)
        {
            var hex = b.ToString("X2");
            sb.Append(hex);
        }
        return sb.ToString();
    }
    public static string ToOctalString(this BigInteger bigint)
    {
        var bytes         = bigint.ToByteArray();
        var index         = bytes.Length - 1;
        var base8         = new StringBuilder((bytes.Length / 3 + 1) * 8);
        var rem           = bytes.Length % 3;
        if (rem == 0) rem = 3;
        var base0         = 0;
        while (rem != 0)
        {
            base0 <<= 8;
            base0 +=  bytes[index--];
            rem--;
        }
        var octal = Convert.ToString(base0, 8);
        if (octal[0] != '0' && bigint.Sign == 1) base8.Append('0');
        base8.Append(octal);
        while (index >= 0)
        {
            base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
            base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
            index -= 3;
        }
        return base8.ToString();
    }
    /// <summary>
    ///     Approximation
    /// </summary>
    public static BigInteger Sqrt(this BigInteger n)
    {
        var q = BigInteger.One << ((int) BigInteger.Log(n, 2) >> 1);
        var m = BigInteger.Zero;
        while (BigInteger.Abs(q - m) >= 1)
        {
            m = q;
            q = (m + n / m) >> 1;
        }
        return q;
    }
}

xIntX.cs

Adjustable Bit Width Big Unsigned or Signed Integer 32,64,128,256,512,1024,2048…

Updated: Jun-11,2021

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(xIntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public struct xIntX : IComparable<xIntX>, IComparable, IEquatable<xIntX>, IConvertible, IFormattable
{
private const int DefaultDataBitWidth = 1024;
private const int DataSize = sizeof(uint);
private const uint AllBits = ~(uint)0;
private const int DataSizeBits = sizeof(uint) * 8;
private const uint HiNeg = (uint)1 << (DataSizeBits - 1);
private static int _dataBitWidth;
private static int DataLength;
private static bool _unsigned;
public uint[] Data;
public xIntX(xIntX value, int bitLength = 0, bool unsigned = false)
{
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
_unsigned = unsigned;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
value.Data.CopyTo(Data, 0);
}
public xIntX(string value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
if (!TryParse(value, out var result))
throw new Exception("TryParse Failed.");
Data = new uint[DataLength];
result.Data.CopyTo(Data, 0);
}
public xIntX(byte value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public xIntX(bool value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = (uint)(value ? 1 : 0);
}
public xIntX(char value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public xIntX(BigDecimal value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
var ba = value.WholePart.ToByteArray();
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var len = ba.Length / DataSize;
var lim = Math.Min(len, DataLength);
Data = new uint[lim];
for (var i = 0; i < lim; i++)
Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
}
public xIntX(BigRational value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
var v1 = value.Numerator.ToByteArray();
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var byteCount = v1.Length;
var isNegative = _unsigned == false && byteCount > 0 && (v1[byteCount - 1] & 0x80) == 0x80;
var unalignedBytes = byteCount % DataSize;
var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
Data = new uint[Math.Max(dwordCount, DataLength)];
if (byteCount == 0)
return;
int curDword, curByte, byteInDword;
curByte = 3;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
byteInDword = 0;
while (byteInDword < DataSize)
{
Data[curDword] <<= 8;
Data[curDword] |= v1[curByte];
curByte--;
byteInDword++;
}
curByte += 8;
}
if (unalignedBytes != 0)
{
if (isNegative)
Data[dwordCount - 1] = 0xffffffff;
for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
Data[curDword] <<= 8;
Data[curDword] |= v1[curByte];
}
}
ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
}
public xIntX(decimal value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0 && !_unsigned)
{
var n = -new xIntX(-value, DataBitWidth, _unsigned);
n.Data.CopyTo(Data, 0);
return;
}
var bits = decimal.GetBits(value);
Data[2] = (uint)bits[2];
Data[1] = (uint)bits[1];
Data[0] = (uint)bits[0];
}
public xIntX(double value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned)
{
}
public xIntX(float value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned)
{
}
public xIntX(short value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0 && !_unsigned)
{
var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint)value;
}
public xIntX(int value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0 && !_unsigned)
{
var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint)value;
}
public xIntX(long value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0 && !_unsigned)
{
var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[1] = (uint)((value >> 32) & 0xffffffff);
Data[0] = (uint)(value & 0xffffffff);
}
public xIntX(sbyte value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0 && !_unsigned)
{
var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint)value;
}
public xIntX(ushort value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public xIntX(uint value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public xIntX(ulong value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[1] = (uint)((value >> 32) & 0xffffffff);
Data[0] = (uint)(value & 0xffffffff);
}
public xIntX(BigInteger value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned)
{
}
public xIntX(Guid value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned)
{
}
public xIntX(byte[] value, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
var minSize = value.Length / DataSize;
if (value == null)
throw new ArgumentNullException("value");
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var byteCount = value.Length;
var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
var unalignedBytes = byteCount % DataSize;
var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
Data = new uint[Math.Max(dwordCount, DataLength)];
if (byteCount == 0)
return;
int curDword, curByte, byteInDword;
curByte = 3;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
byteInDword = 0;
while (byteInDword < DataSize)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
curByte--;
byteInDword++;
}
curByte += 8;
}
if (unalignedBytes != 0)
{
if (isNegative)
Data[dwordCount - 1] = 0xffffffff;
for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
}
}
}
public xIntX(int sign, uint[] array, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (array == null)
throw new Exception("Array cannot be null.");
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
var ba = new byte[DataSize];
for (var i = 0; i < Math.Min(DataLength, array.Length); i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
if (!_unsigned)
{
if (sign < 0)
Data[DataLength - 1] |= HiNeg;
else
Data[DataLength - 1] &= ~HiNeg;
}
}
public xIntX(uint[] array, int bitLength = 0, bool unsigned = false)
{
_unsigned = unsigned;
if (array == null)
throw new Exception("Array cannot be null.");
if (bitLength == 0)
DataBitWidth = DefaultDataBitWidth;
else DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
if (array.Length != DataLength)
Array.Resize(ref array, DataLength);
Data = new uint[DataLength];
var ba = new byte[DataSize];
for (var i = 0; i < Data.Length; i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
}
public bool Unsigned
{
get => _unsigned;
set => _unsigned = value;
}
private static int DataBitWidth
{
get => _dataBitWidth;
set
{
_dataBitWidth = value;
if (_dataBitWidth < 32)
_dataBitWidth = 32;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public xIntX MaxValue
{
get
{
var r = new xIntX(0, DataBitWidth, _unsigned);
for (var i = 0; i < r.Data.Length; ++i)
r.Data[i] = uint.MaxValue;
r.Data[r.Data.Length - 1] = int.MaxValue;
return r;
}
}
public int BitWidth
{
get
{
xIntX bw = 1;
var v = new xIntX(this, DataBitWidth, _unsigned);
while ((v >>= 1) > 0)
bw++;
if (bw < 8)
bw = 8;
while (bw % 8 != 0)
bw++;
return (int)bw;
}
}
public float MaxDecimalPlaces => DataBitWidth / 64f * 20f;
public int DecimalPlaces
{
get
{
var a = new xIntX(this, DataBitWidth, _unsigned);
var dPlaces = 0;
if (a.Sign == 0)
return 1;
if (a.Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new xIntX(10, DataBitWidth, _unsigned);
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
a = quotient;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
}
public int Sign
{
get
{
if (_unsigned)
return 1;
var allZero = true;
var ba = Data;
for (var i = 0; i < ba.Length; i++)
if (ba[i] != 0)
{
allZero = false;
break;
}
if (allZero)
return 0;
return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1;
}
}
public bool IsOne => this == 1;
public bool IsEven => (this & 1) == 0;
public bool IsNegative => Sign < 0;
public bool IsZero
{
get
{
for (var i = 0; i < Data.Length; i++)
if (Data[i] != 0)
return false;
return true;
}
}
public int DataUsed
{
get
{
var DataUsed = Data.Length;
if (!IsNegative)
{
while (DataUsed > 1 && Data[DataUsed - 1] == 0)
--DataUsed;
if (DataUsed == 0)
DataUsed = 1;
}
return DataUsed;
}
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public int CompareTo(xIntX value)
{
return Compare(this, value);
}
TypeCode IConvertible.GetTypeCode()
{
return TypeCode.Object;
}
bool IConvertible.ToBoolean(IFormatProvider provider)
{
return (bool)this;
}
byte IConvertible.ToByte(IFormatProvider provider)
{
return (byte)this;
}
char IConvertible.ToChar(IFormatProvider provider)
{
return (char)this;
}
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
throw new InvalidCastException();
}
decimal IConvertible.ToDecimal(IFormatProvider provider)
{
return (decimal)this;
}
double IConvertible.ToDouble(IFormatProvider provider)
{
return (double)this;
}
short IConvertible.ToInt16(IFormatProvider provider)
{
return (short)this;
}
int IConvertible.ToInt32(IFormatProvider provider)
{
return (int)this;
}
long IConvertible.ToInt64(IFormatProvider provider)
{
return (long)this;
}
sbyte IConvertible.ToSByte(IFormatProvider provider)
{
return (sbyte)this;
}
float IConvertible.ToSingle(IFormatProvider provider)
{
return (float)this;
}
string IConvertible.ToString(IFormatProvider provider)
{
return ToString(null, provider);
}
public object ToType(Type conversionType, IFormatProvider provider)
{
object value;
if (TryConvert(conversionType, provider, out value))
return value;
throw new InvalidCastException();
}
ushort IConvertible.ToUInt16(IFormatProvider provider)
{
if (Data[1] != 0)
throw new OverflowException();
return Convert.ToUInt16(Data[0]);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
if (Data[1] != 0)
throw new OverflowException();
return Convert.ToUInt32(Data[0]);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
if (Data[1] != 0)
return ((ulong)Data[1] << 32) | Data[0];
return Data[0];
}
public bool Equals(xIntX obj)
{
if (ReferenceEquals(obj, null))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Data.Length != obj.Data.Length)
return false;
if (Sign != obj.Sign)
return false;
for (var i = 0; i < Data.Length; i++)
if (Data[i] != obj.Data[i])
return false;
return true;
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (formatProvider == null)
formatProvider = CultureInfo.CurrentCulture;
if (!string.IsNullOrEmpty(format))
{
var ch = format[0];
if (ch == 'x' || ch == 'X')
{
int.TryParse(format.Substring(1).Trim(), out var min);
return ToHexString(ch == 'X');
}
if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
throw new NotSupportedException("Not supported format: " + format);
}
return ToString((NumberFormatInfo)formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
}
private static byte[] ToByteArray(ulong[] value)
{
var ba = new byte[value.Length << 3];
Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
return ba;
}
private static byte[] ToByteArray(uint[] value)
{
var ba = new byte[value.Length << 2];
Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
return ba;
}
public override int GetHashCode()
{
static uint CombineHash(uint u1, uint u2)
{
return ((u1 << 7) | (u1 >> 25)) ^ u2;
}
var s = Sign;
var i = Data.Length;
while (--i >= 0)
s = (int)CombineHash((uint)s, Data[i]);
return s;
}
public static byte[] GetBytesInt(xIntX value)
{
var b = value.Sign.GetBytes();
var tb = b.Add(value.Data.GetBytes());
return tb;
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
public string ToHexString(bool caps)
{
var bytes = ToByteArray().Invert();
var sb = new StringBuilder();
var x = caps ? "X" : "x";
foreach (var b in bytes)
{
var hex = b.ToString($"{x}2");
sb.Append(hex);
}
return sb.ToString();
}
private string ToString(NumberFormatInfo info, int radix)
{
if (radix < 2 || radix > 36)
throw new ArgumentOutOfRangeException("radix");
if (Sign == 0)
return "0";
var negative = Sign < 0;
var a = new xIntX(this, DataBitWidth, _unsigned);
if (negative)
try
{
a = -a;
}
catch (Exception ex)
{
}
var biRadix = new xIntX(radix, DataBitWidth, _unsigned);
const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
var al = new ArrayList();
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
al.Insert(0, charSet[(int)remainder.Data[0]]);
a = quotient;
}
catch (Exception ex)
{
break;
}
var result = new string((char[])al.ToArray(typeof(char)));
if (radix == 10 && negative)
return "-" + result;
return result;
}
public static xIntX Abs(xIntX value)
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException("value");
if (value.Sign < 0)
return -value;
return value;
}
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 xIntX Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static xIntX Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static xIntX Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static xIntX 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 xIntX result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out xIntX result)
{
result = 0;
if (string.IsNullOrEmpty(value))
return false;
if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(1);
}
else
{
if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(2);
}
}
if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
return TryParseNum(value, 16, out result);
return TryParseNum(value, 10, out result);
}
public static bool TryParseNum(string digits, int radix, out xIntX result)
{
result = new xIntX(0, DataBitWidth, _unsigned);
if (digits == null)
return false;
var multiplier = new xIntX(1, DataBitWidth * 2, _unsigned);
digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
var nDigits = digits[0] == '-' ? 1 : 0;
for (var idx = digits.Length - 1; idx >= nDigits; idx--)
{
var d = (int)digits[idx];
if (d != 48)
{
var a = 1;
}
if (d >= '0' && d <= '9')
d -= '0';
else if (d >= 'A' && d <= 'Z')
d = d - 'A' + 10;
else
return false;
if (d >= radix)
return false;
result += multiplier * d;
multiplier *= radix;
if (multiplier.DataUsed > DataLength)
throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
}
if (digits[0] == '-' && !_unsigned)
result = -result;
return true;
}
public static int Compare(xIntX left, object right)
{
if (right is xIntX)
return Compare(left, (xIntX)right);
if (right is bool)
return Compare(left, new xIntX((bool)right, DataBitWidth, _unsigned));
if (right is byte)
return Compare(left, new xIntX((byte)right, DataBitWidth, _unsigned));
if (right is char)
return Compare(left, new xIntX((char)right, DataBitWidth, _unsigned));
if (right is decimal)
return Compare(left, new xIntX((decimal)right, DataBitWidth, _unsigned));
if (right is double)
return Compare(left, new xIntX((double)right, DataBitWidth, _unsigned));
if (right is short)
return Compare(left, new xIntX((short)right, DataBitWidth, _unsigned));
if (right is int)
return Compare(left, new xIntX((int)right, DataBitWidth, _unsigned));
if (right is long)
return Compare(left, new xIntX((long)right, DataBitWidth, _unsigned));
if (right is sbyte)
return Compare(left, new xIntX((sbyte)right, DataBitWidth, _unsigned));
if (right is float)
return Compare(left, new xIntX((float)right, DataBitWidth, _unsigned));
if (right is ushort)
return Compare(left, new xIntX((ushort)right, DataBitWidth, _unsigned));
if (right is uint)
return Compare(left, new xIntX((uint)right, DataBitWidth, _unsigned));
if (right is ulong)
return Compare(left, new xIntX((ulong)right, DataBitWidth, _unsigned));
var bytes = right as byte[];
if (bytes != null)
return Compare(left, new xIntX(bytes, DataBitWidth, _unsigned));
if (right is Guid)
return Compare(left, new xIntX((Guid)right, DataBitWidth, _unsigned));
throw new ArgumentException();
}
public static int Compare(xIntX left, xIntX right)
{
if (ReferenceEquals(left, right))
return 0;
if (left.Sign >= 0 && right.Sign < 0)
return 1;
if (left.Sign < 0 && right.Sign >= 0)
return -1;
if (left.Data.Length != right.Data.Length)
return -1;
for (var i = left.Data.Length - 1; i > 0; i--)
if (left.Data[i] != right.Data[i])
return left.Data[i].CompareTo(right.Data[i]);
return left.Data[0].CompareTo(right.Data[0]);
}
public static implicit operator xIntX(bool value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(byte value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(char value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static explicit operator xIntX(decimal value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static explicit operator xIntX(double value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(short value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(int value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(long value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(sbyte value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static explicit operator xIntX(float value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(ushort value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(uint value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(ulong value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(BigInteger value)
{
if (DataBitWidth == 0)
DataBitWidth = value.GetBitWidth();
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(BigRational value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static implicit operator xIntX(BigDecimal value)
{
return new xIntX(value, DataBitWidth, _unsigned);
}
public static explicit operator bool(xIntX value)
{
return (byte)value.Data[0] != 0;
}
public static explicit operator byte(xIntX value)
{
return (byte)value.Data[0];
}
public static explicit operator char(xIntX value)
{
return (char)(ushort)value.Data[0];
}
public static explicit operator decimal(xIntX value)
{
if (value.Sign == 0)
return 0;
if (value.Data.Length == 1)
return new decimal((int)value.Data[0], 0, 0, value.Sign < 0, 0);
if (value.Data.Length == 2)
return new decimal((int)value.Data[0], (int)value.Data[1], 0, value.Sign < 0, 0);
if (value.Data.Length == 3)
return new decimal((int)value.Data[0], (int)value.Data[1], (int)value.Data[2], value.Sign < 0, 0);
throw new ArgumentException("Value length exceeds decimal length.");
}
public static explicit operator double(xIntX value)
{
if (value.Sign == 0)
return 0;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
throw new OverflowException();
return d;
}
public static explicit operator float(xIntX value)
{
if (value.Sign == 0)
return 0;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
throw new OverflowException();
return f;
}
public static explicit operator short(xIntX value)
{
if (value.Data[0] > 0x8000)
throw new OverflowException();
if (value.Data[0] == 0x8000 && value.Sign > 0)
throw new OverflowException();
return (short)((int)value.Data[0] * value.Sign);
}
public static explicit operator int(xIntX value)
{
if (value.Sign == 0)
return 0;
return (int)value.Data[0] * value.Sign;
}
public static explicit operator long(xIntX value)
{
if (value.Sign == 0)
return 0;
if (value.Data[0] > int.MaxValue)
throw new OverflowException();
if (value.Data.Length > 1)
if (value.Data[1] != 0)
return (long)(((ulong)value.Data[1] << 32) | value.Data[0]) * value.Sign;
return value.Data[0] * value.Sign;
}
public static explicit operator uint(xIntX value)
{
if (value.Sign == 0)
return 0;
return value.Data[0];
}
public static explicit operator ushort(xIntX value)
{
if (value.Sign == 0)
return 0;
return (ushort)value.Data[0];
}
public static explicit operator ulong(xIntX value)
{
if (value.Data.Length > 1)
if (value.Data[1] != 0)
return ((ulong)value.Data[1] << 32) | value.Data[0];
return value.Data[0];
}
public static explicit operator BigInteger(xIntX value)
{
return new BigInteger(value.ToByteArray());
}
public static explicit operator BigDecimal(xIntX value)
{
return new xIntX(value.ToByteArray());
}
public static explicit operator BigRational(xIntX value)
{
return new BigRational(new BigInteger(value.ToByteArray()));
}
public static bool operator >(xIntX left, xIntX right)
{
return left.CompareTo(right) > 0;
}
public static bool operator <(xIntX left, xIntX right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(xIntX left, xIntX right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(xIntX left, xIntX right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(xIntX left, xIntX right)
{
return !left.Equals(right);
}
public static bool operator ==(xIntX left, xIntX right)
{
return left.Equals(right);
}
public static xIntX operator +(xIntX value)
{
return value;
}
public static xIntX operator ~(xIntX value)
{
var da = new uint[DataLength];
for (var idx = 0; idx < DataLength; idx++)
da[idx] = ~value.Data[idx];
return new xIntX(da, DataBitWidth);
}
public static xIntX operator -(xIntX value)
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException("value");
if (value.IsZero)
return 0;
var da = new uint[DataLength];
for (var i = 0; i < da.Length; i++)
da[i] = ~value.Data[i];
var carry = true;
var index = 0;
while (carry && index < da.Length)
{
var val = (long)da[index] + 1;
da[index] = (uint)(val & AllBits);
carry = val >> DataSizeBits > 0;
index++;
}
return new xIntX(da, DataBitWidth);
}
public static xIntX operator ++(xIntX value)
{
return value + 1;
}
public static xIntX operator --(xIntX value)
{
return value - 1;
}
public static xIntX Negate(xIntX value)
{
var ldata = (uint[])value.Data.Clone();
for (var i = 0; i < value.Data.Length; i++)
ldata[i] = ~value.Data[i];
return new xIntX(value.Sign, ldata, DataBitWidth, _unsigned);
}
public static xIntX operator +(xIntX left, xIntX right)
{
if (right.IsZero)
return left;
if (left.IsZero)
return right;
var dl = Math.Max(left.Data.Length, right.Data.Length);
var lim = Math.Min(left.Data.Length, right.Data.Length);
var result = new uint[dl];
long carry = 0;
for (var i = 0; i < dl && i < lim; i++)
{
var sum = left.Data[i] + (long)right.Data[i] + carry;
carry = sum >> 32;
result[i] = (uint)(sum & 0xFFFFFFFF);
}
if (carry != 0)
{
var idx = 0;
while (idx < result.Length - 1)
{
if (result[idx] == 0)
break;
idx++;
}
result[idx] = (uint)carry;
}
return new xIntX(left.Sign * right.Sign, result, DataBitWidth, _unsigned);
}
public static xIntX operator -(xIntX left, xIntX right)
{
if (right.IsZero)
return left;
if (left.IsZero)
return -right;
var size = Math.Max(left.Data.Length, right.Data.Length) + 1;
var da = new uint[size];
long carry = 0;
for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++)
{
var diff = left.Data[i] - (long)right.Data[i] - carry;
da[i] = (uint)(diff & AllBits);
carry = diff < 0 ? 1 : 0;
}
return new xIntX(da, DataBitWidth);
}
public static xIntX Add(xIntX left, xIntX right)
{
return left + right;
}
public static xIntX Subtract(xIntX left, xIntX right)
{
return left - right;
}
public static xIntX Divide(xIntX dividend, xIntX divisor)
{
if (divisor == 0)
throw new DivideByZeroException();
return DivRem(dividend, divisor, out var integer);
}
public static void Divide(xIntX dividend, xIntX divisor, out xIntX remainder, out xIntX quotient)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
remainder = new xIntX(1, rem, DataBitWidth, _unsigned);
quotient = new xIntX(dividend.Sign * divisor.Sign, quo, DataBitWidth, _unsigned);
}
public static xIntX DivRem(xIntX dividend, xIntX divisor, out xIntX remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
remainder = new xIntX(1, rem, DataBitWidth, _unsigned);
return new xIntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth, _unsigned);
}
private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
{
const ulong hiBit = 0x100000000;
var divisorLen = GetLength(divisor);
var dividendLen = GetLength(dividend);
if (divisorLen <= 1)
{
ulong rem = 0;
var div = divisor[0];
quotient = new uint[dividendLen];
remainder = new uint[1];
for (var i = dividendLen - 1; i >= 0; i--)
{
rem *= hiBit;
rem += dividend[i];
var q = rem / div;
rem -= q * div;
quotient[i] = (uint)q;
}
remainder[0] = (uint)rem;
return;
}
if (dividendLen >= divisorLen)
{
var shift = GetNormalizeShift(divisor[divisorLen - 1]);
var normDividend = new uint[dividendLen + 1];
var normDivisor = new uint[divisorLen];
Normalize(dividend, dividendLen, normDividend, shift);
Normalize(divisor, divisorLen, normDivisor, shift);
quotient = new uint[dividendLen - divisorLen + 1];
for (var j = dividendLen - divisorLen; j >= 0; j--)
{
var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
var qj = dx / normDivisor[divisorLen - 1];
dx -= qj * normDivisor[divisorLen - 1];
do
{
if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
break;
qj -= 1L;
dx += normDivisor[divisorLen - 1];
} while (dx < hiBit);
ulong di = 0;
ulong dj;
var index = 0;
while (index < divisorLen)
{
var dqj = normDivisor[index] * qj;
dj = normDividend[index + j] - (uint)dqj - di;
normDividend[index + j] = (uint)dj;
dqj = dqj >> 32;
dj = dj >> 32;
di = dqj - dj;
index++;
}
dj = normDividend[j + divisorLen] - di;
normDividend[j + divisorLen] = (uint)dj;
quotient[j] = (uint)qj;
if ((long)dj < 0)
{
quotient[j]--;
ulong sum = 0;
for (index = 0; index < divisorLen; index++)
{
sum = normDivisor[index] + normDividend[j + index] + sum;
normDividend[j + index] = (uint)sum;
sum = sum >> 32;
}
sum += normDividend[j + divisorLen];
normDividend[j + divisorLen] = (uint)sum;
}
}
remainder = Unnormalize(normDividend, shift);
return;
}
quotient = new uint[1];
remainder = dividend;
}
private static int GetLength(uint[] uints)
{
var index = uints.Length - 1;
while (index >= 0 && uints[index] == 0)
index--;
return index + 1;
}
private static int GetNormalizeShift(uint ui)
{
var shift = 0;
if ((ui & 0xffff0000) == 0)
{
ui = ui << 16;
shift += 16;
}
if ((ui & 0xff000000) == 0)
{
ui = ui << 8;
shift += 8;
}
if ((ui & 0xf0000000) == 0)
{
ui = ui << 4;
shift += 4;
}
if ((ui & 0xc0000000) == 0)
{
ui = ui << 2;
shift += 2;
}
if ((ui & 0x80000000) == 0)
shift++;
return shift;
}
private static uint[] Unnormalize(uint[] normalized, int shift)
{
var len = GetLength(normalized);
var unnormalized = new uint[len];
if (shift > 0)
{
var rshift = 32 - shift;
uint r = 0;
for (var i = len - 1; i >= 0; i--)
{
unnormalized[i] = (normalized[i] >> shift) | r;
r = normalized[i] << rshift;
}
}
else
{
for (var j = 0; j < len; j++)
unnormalized[j] = normalized[j];
}
return unnormalized;
}
private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
{
int i;
uint n = 0;
if (shift > 0)
{
var rShift = 32 - shift;
for (i = 0; i < len; i++)
{
normalized[i] = (unormalized[i] << shift) | n;
n = unormalized[i] >> rShift;
}
}
else
{
i = 0;
while (i < len)
{
normalized[i] = unormalized[i];
i++;
}
}
while (i < normalized.Length)
normalized[i++] = 0;
if (n != 0)
normalized[len] = n;
}
public static xIntX Remainder(xIntX dividend, xIntX divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static xIntX Max(xIntX left, xIntX right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static xIntX Min(xIntX left, xIntX right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static xIntX operator %(xIntX dividend, xIntX divisor)
{
return Remainder(dividend, divisor);
}
public static xIntX operator /(xIntX dividend, xIntX divisor)
{
return Divide(dividend, divisor);
}
public ulong[] ToUIn64Array()
{
var al = Data.Length >> 1;
if (al * 2 != Data.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2);
return arr;
}
public uint[] ToUIn32Array()
{
return Data;
}
public byte[] ToByteArray()
{
var ba = new byte[Data.Length * DataSize];
Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize);
return ba;
}
private void TrimToMsb()
{
var dataUsed = Data.Length;
while (dataUsed > 1 && Data[dataUsed - 1] == 0)
--dataUsed;
if (dataUsed != Data.Length)
{
var tData = new uint[dataUsed];
for (var i = 0; i < dataUsed; i++)
tData[i] = Data[i];
Data = (uint[])tData.Clone();
}
}
public static xIntX Multiply(xIntX left, xIntX right)
{
if (left == 0 || right == 0)
return 0;
if (left == 1 && right != 1)
return right;
if (left != 1 && right == 1)
return left;
if (left == 1 && right == 1)
return 1;
var xInts = left.Data;
var yInts = right.Data;
var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
for (var i = 0; i < xInts.Length; i++)
{
var index = i;
ulong remainder = 0;
foreach (var yi in yInts)
{
remainder = remainder + (ulong)xInts[i] * yi + mulInts[index];
mulInts[index++] = (uint)remainder;
remainder = remainder >> 32;
}
while (remainder != 0)
{
remainder += mulInts[index];
mulInts[index++] = (uint)remainder;
remainder = remainder >> 32;
}
}
return new xIntX(left.Sign * right.Sign, mulInts, DataBitWidth, _unsigned);
}
public static xIntX operator *(xIntX left, xIntX right)
{
return Multiply(left, right);
}
public static xIntX operator >> (xIntX value, int shift)
{
if (shift == 0)
return value;
if (shift == int.MinValue)
return value << int.MaxValue << 1;
if (shift < 0)
return value << -shift;
var xd = value.Data;
var shiftAmount = 32;
var invShift = 0;
var bufLen = xd.Length;
while (bufLen > 1 && xd[bufLen - 1] == 0)
bufLen--;
for (var count = shift; count > 0; count -= shiftAmount)
{
if (count < shiftAmount)
{
shiftAmount = count;
invShift = 32 - shiftAmount;
}
ulong carry = 0;
for (var i = bufLen - 1; i >= 0; i--)
{
var val = (ulong)xd[i] >> shiftAmount;
val |= carry;
carry = (ulong)xd[i] << invShift;
xd[i] = (uint)val;
}
}
return new xIntX(value.Sign, xd, DataBitWidth, _unsigned);
}
public static xIntX operator <<(xIntX value, int shift)
{
if (shift == 0)
return value;
if (shift == int.MinValue)
return value >> int.MaxValue >> 1;
if (shift < 0)
return value >> -shift;
var digitShift = shift / 32;
var smallShift = shift - digitShift * 32;
var xd = value.Data;
var xl = xd.Length;
var zd = new uint[xl + digitShift + 1];
if (smallShift == 0)
{
for (var index = 0; index < xl; ++index)
zd[index + digitShift] = xd[index];
}
else
{
var carryShift = 32 - smallShift;
uint carry = 0;
int index;
for (index = 0; index < xl; ++index)
{
var rot = xd[index];
zd[index + digitShift] = (rot << smallShift) | carry;
carry = rot >> carryShift;
}
zd[index + digitShift] = carry;
}
return new xIntX(value.Sign, zd, DataBitWidth, _unsigned);
}
public static xIntX operator |(xIntX left, xIntX right)
{
if (left == 0)
return right;
if (right == 0)
return left;
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu | yu;
}
return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
}
public static xIntX operator ^(xIntX left, xIntX right)
{
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu ^ yu;
}
return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
}
public static xIntX operator &(xIntX left, xIntX right)
{
if (left == 0 || right == 0)
return 0;
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu & yu;
}
return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
}
public string ToBinaryString()
{
var bytes = ToByteArray();
var index = bytes.Length - 1;
var base2 = new StringBuilder(bytes.Length * 8);
var binary = Convert.ToString(bytes[index], 2);
if (binary[0] != '0' && Sign == 1) base2.Append('0');
base2.Append(binary);
for (index--; index >= 0; index--)
base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
return base2.ToString();
}
public string ToOctalString()
{
var bytes = ToByteArray();
var index = bytes.Length - 1;
var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8);
var rem = bytes.Length % 3;
if (rem == 0) rem = 3;
var base0 = 0;
while (rem != 0)
{
base0 <<= 8;
base0 += bytes[index--];
rem--;
}
var octal = Convert.ToString(base0, 8);
if (octal[0] != '0' && Sign == 1) base8.Append('0');
base8.Append(octal);
while (index >= 0)
{
base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
index -= 3;
}
return base8.ToString();
}
public static xIntX Pow(xIntX value, xIntX exponent, int bitLength)
{
if (value == null)
throw new ArgumentNullException("Value cannot be null");
if (exponent == null)
throw new ArgumentNullException("Exponent cannot be null");
if (exponent < 0)
throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative");
var result = new xIntX("1", bitLength, _unsigned);
while (exponent != 0)
{
if ((exponent & 1) != 0)
result *= value;
exponent >>= 1;
value *= value;
}
return result;
}
/// <summary>
/// Works well, not as good as BigInteger
/// </summary>
public static BigInteger ModPow(BigInteger n, BigInteger e, BigInteger m)
{
var n1 = n;
var e1 = e;
if (e1 == 0)
return 1;
if (e1 == 1)
return n1 % m;
if (e1 == 2)
return n1 * n1 % m;
n1 %= m;
BigInteger r = 1;
if ((e1 & 1) == 1)
r = n1;
while (e1 > 1)
{
e1 >>= 1;
n1 = n1 * n1 % m;
if ((e1 & 1) == 1)
r = r * n1 % m;
}
return r;
}
public static int GetSign(uint[] value)
{
var allZero = true;
for (var i = 0; i < value.Length; i++)
if (value[i] != 0)
{
allZero = false;
break;
}
if (allZero)
return 0;
return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1;
}
private static int GetDataUsed(uint[] array)
{
var neg = GetSign(array) < 0;
var dataUsed = array.Length;
if (!neg)
{
while (dataUsed > 1 && array[dataUsed - 1] == 0)
--dataUsed;
if (dataUsed == 0)
dataUsed = 1;
}
return dataUsed;
}
public int GetDecimalPlaces(xIntX a)
{
var dPlaces = 0;
if (a.Sign == 0)
return 1;
if (a.Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new xIntX(10, DataBitWidth, _unsigned);
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
a = quotient;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
private uint[] TwosComplement(uint[] d)
{
var i = 0;
uint v = 0;
for (; i < d.Length; i++)
{
v = ~d[i] + 1;
d[i] = v;
if (v != 0)
{
i++;
break;
}
}
if (v != 0)
{
for (; i < d.Length; i++)
d[i] = ~d[i];
}
else
{
Array.Resize(ref d, d.Length + 1);
d[d.Length - 1] = 1;
}
return d;
}
public (xIntX approximateRoot, BigRational realRoot) Sqrt()
{
var n = (BigRational)this;
var r = n.Sqrt();
return (r.WholePart, r);
}
public xIntX Pow(int e)
{
var ans = this;
if (e == 1)
return ans;
if (e == 0)
return 1;
for (var i = 1; i != e; i++)
ans *= this;
return ans;
}
public static double Log(xIntX value, double baseValue)
{
var c = 0.0;
var d = 0.5;
var dataLength = value.DataUsed;
var topBits = 0;
var x = value.Data[dataLength - 1];
while (x > 0)
{
x >>= 1;
topBits++;
}
var bitLength = (dataLength - 1) * 32 + topBits;
var bit = (uint)(1 << (topBits - 1));
for (var index = dataLength - 1; index >= 0; --index)
{
for (; bit != 0U; bit >>= 1)
{
if (((int)value.Data[index] & (int)bit) != 0)
c += d;
d *= 0.5;
}
bit = 2147483648U;
}
return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue);
}
public static List<xIntX> GetFactors(xIntX n)
{
var Factors = new List<xIntX>();
var s = (xIntX)1 << ((int)Math.Ceiling(Log(n, 2)) >> 1);
var a = (xIntX)3;
while (a < s)
{
if (n % a == 0)
{
Factors.Add(a);
if (a * a != n)
Factors.Add(n / a);
}
a += 2;
}
return Factors;
}
public static xIntX GreatestCommonDivisor(xIntX a, xIntX b)
{
while (b > 0)
{
var r = a % b;
a = b;
b = r;
}
return a;
}
public static xIntX LeastCommonMultiple(xIntX a, xIntX b)
{
return a * b / a.Gcd(b);
}
public static double Log10(xIntX value)
{
return Log(value, 10.0);
}
public static double LogN(xIntX value)
{
return Log(value, 2.0);
}
public void ConstructFromArray(byte[] value, int bitLength)
{
var minSize = value.Length / DataSize;
if (value == null)
throw new ArgumentNullException("value");
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var byteCount = value.Length;
var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
var unalignedBytes = byteCount % DataSize;
var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
Data = new uint[Math.Max(dwordCount, DataLength)];
if (byteCount == 0)
return;
int curDword, curByte, byteInDword;
curByte = 3;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
byteInDword = 0;
while (byteInDword < DataSize)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
curByte--;
byteInDword++;
}
curByte += 8;
}
if (unalignedBytes != 0)
{
if (isNegative)
Data[dwordCount - 1] = 0xffffffff;
for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
}
}
}
private class xIntXConverter : 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 xIntX(0, DataBitWidth, _unsigned);
}
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);
}
}
}
public class xIntXComparer : IComparer<xIntX>
{
public int Compare(xIntX left, xIntX right)
{
return left.CompareTo(right);
}
public bool Equals(xIntX left, xIntX right)
{
if (left == null || right == null)
return false;
return left.Equals(right);
}
public int GetHashCode(xIntX obj)
{
return obj.GetHashCode();
}
}
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(xIntXConverter))] [DebuggerDisplay("{DDisplay}")] public struct xIntX : IComparable<xIntX>, IComparable, IEquatable<xIntX>, IConvertible, IFormattable { private const int DefaultDataBitWidth = 1024; private const int DataSize = sizeof(uint); private const uint AllBits = ~(uint)0; private const int DataSizeBits = sizeof(uint) * 8; private const uint HiNeg = (uint)1 << (DataSizeBits - 1); private static int _dataBitWidth; private static int DataLength; private static bool _unsigned; public uint[] Data; public xIntX(xIntX value, int bitLength = 0, bool unsigned = false) { if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; _unsigned = unsigned; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; value.Data.CopyTo(Data, 0); } public xIntX(string value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); Data = new uint[DataLength]; result.Data.CopyTo(Data, 0); } public xIntX(byte value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(bool value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = (uint)(value ? 1 : 0); } public xIntX(char value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(BigDecimal value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var ba = value.WholePart.ToByteArray(); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var len = ba.Length / DataSize; var lim = Math.Min(len, DataLength); Data = new uint[lim]; for (var i = 0; i < lim; i++) Data[i] = BitConverter.ToUInt32(ba, i * DataSize); } public xIntX(BigRational value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var v1 = value.Numerator.ToByteArray(); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = v1.Length; var isNegative = _unsigned == false && byteCount > 0 && (v1[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= v1[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= v1[curByte]; } } ConstructFromArray(value.Numerator.ToByteArray(), bitLength); } public xIntX(decimal value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-value, DataBitWidth, _unsigned); n.Data.CopyTo(Data, 0); return; } var bits = decimal.GetBits(value); Data[2] = (uint)bits[2]; Data[1] = (uint)bits[1]; Data[0] = (uint)bits[0]; } public xIntX(double value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned) { } public xIntX(float value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned) { } public xIntX(short value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(int value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(long value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[1] = (uint)((value >> 32) & 0xffffffff); Data[0] = (uint)(value & 0xffffffff); } public xIntX(sbyte value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0 && !_unsigned) { var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint)value; } public xIntX(ushort value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(uint value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public xIntX(ulong value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[1] = (uint)((value >> 32) & 0xffffffff); Data[0] = (uint)(value & 0xffffffff); } public xIntX(BigInteger value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned) { } public xIntX(Guid value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned) { } public xIntX(byte[] value, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } public xIntX(int sign, uint[] array, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (array == null) throw new Exception("Array cannot be null."); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Math.Min(DataLength, array.Length); i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } if (!_unsigned) { if (sign < 0) Data[DataLength - 1] |= HiNeg; else Data[DataLength - 1] &= ~HiNeg; } } public xIntX(uint[] array, int bitLength = 0, bool unsigned = false) { _unsigned = unsigned; if (array == null) throw new Exception("Array cannot be null."); if (bitLength == 0) DataBitWidth = DefaultDataBitWidth; else DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (array.Length != DataLength) Array.Resize(ref array, DataLength); Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Data.Length; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } } public bool Unsigned { get => _unsigned; set => _unsigned = value; } private static int DataBitWidth { get => _dataBitWidth; set { _dataBitWidth = value; if (_dataBitWidth < 32) _dataBitWidth = 32; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public xIntX MaxValue { get { var r = new xIntX(0, DataBitWidth, _unsigned); for (var i = 0; i < r.Data.Length; ++i) r.Data[i] = uint.MaxValue; r.Data[r.Data.Length - 1] = int.MaxValue; return r; } } public int BitWidth { get { xIntX bw = 1; var v = new xIntX(this, DataBitWidth, _unsigned); while ((v >>= 1) > 0) bw++; if (bw < 8) bw = 8; while (bw % 8 != 0) bw++; return (int)bw; } } public float MaxDecimalPlaces => DataBitWidth / 64f * 20f; public int DecimalPlaces { get { var a = new xIntX(this, DataBitWidth, _unsigned); var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new xIntX(10, DataBitWidth, _unsigned); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } } public int Sign { get { if (_unsigned) return 1; var allZero = true; var ba = Data; for (var i = 0; i < ba.Length; i++) if (ba[i] != 0) { allZero = false; break; } if (allZero) return 0; return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1; } } public bool IsOne => this == 1; public bool IsEven => (this & 1) == 0; public bool IsNegative => Sign < 0; public bool IsZero { get { for (var i = 0; i < Data.Length; i++) if (Data[i] != 0) return false; return true; } } public int DataUsed { get { var DataUsed = Data.Length; if (!IsNegative) { while (DataUsed > 1 && Data[DataUsed - 1] == 0) --DataUsed; if (DataUsed == 0) DataUsed = 1; } return DataUsed; } } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public int CompareTo(xIntX value) { return Compare(this, value); } TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } bool IConvertible.ToBoolean(IFormatProvider provider) { return (bool)this; } byte IConvertible.ToByte(IFormatProvider provider) { return (byte)this; } char IConvertible.ToChar(IFormatProvider provider) { return (char)this; } DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return (decimal)this; } double IConvertible.ToDouble(IFormatProvider provider) { return (double)this; } short IConvertible.ToInt16(IFormatProvider provider) { return (short)this; } int IConvertible.ToInt32(IFormatProvider provider) { return (int)this; } long IConvertible.ToInt64(IFormatProvider provider) { return (long)this; } sbyte IConvertible.ToSByte(IFormatProvider provider) { return (sbyte)this; } float IConvertible.ToSingle(IFormatProvider provider) { return (float)this; } string IConvertible.ToString(IFormatProvider provider) { return ToString(null, provider); } public object ToType(Type conversionType, IFormatProvider provider) { object value; if (TryConvert(conversionType, provider, out value)) return value; throw new InvalidCastException(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { if (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt16(Data[0]); } uint IConvertible.ToUInt32(IFormatProvider provider) { if (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt32(Data[0]); } ulong IConvertible.ToUInt64(IFormatProvider provider) { if (Data[1] != 0) return ((ulong)Data[1] << 32) | Data[0]; return Data[0]; } public bool Equals(xIntX obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; if (Data.Length != obj.Data.Length) return false; if (Sign != obj.Sign) return false; for (var i = 0; i < Data.Length; i++) if (Data[i] != obj.Data[i]) return false; return true; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider == null) formatProvider = CultureInfo.CurrentCulture; if (!string.IsNullOrEmpty(format)) { var ch = format[0]; if (ch == 'x' || ch == 'X') { int.TryParse(format.Substring(1).Trim(), out var min); return ToHexString(ch == 'X'); } if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd') throw new NotSupportedException("Not supported format: " + format); } return ToString((NumberFormatInfo)formatProvider.GetFormat(typeof(NumberFormatInfo)), 10); } private static byte[] ToByteArray(ulong[] value) { var ba = new byte[value.Length << 3]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3); return ba; } private static byte[] ToByteArray(uint[] value) { var ba = new byte[value.Length << 2]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2); return ba; } public override int GetHashCode() { static uint CombineHash(uint u1, uint u2) { return ((u1 << 7) | (u1 >> 25)) ^ u2; } var s = Sign; var i = Data.Length; while (--i >= 0) s = (int)CombineHash((uint)s, Data[i]); return s; } public static byte[] GetBytesInt(xIntX value) { var b = value.Sign.GetBytes(); var tb = b.Add(value.Data.GetBytes()); return tb; } public override bool Equals(object obj) { return base.Equals(obj); } public override string ToString() { return ToString(null, null); } public string ToString(string format) { return ToString(format, null); } public string ToHexString(bool caps) { var bytes = ToByteArray().Invert(); var sb = new StringBuilder(); var x = caps ? "X" : "x"; foreach (var b in bytes) { var hex = b.ToString($"{x}2"); sb.Append(hex); } return sb.ToString(); } private string ToString(NumberFormatInfo info, int radix) { if (radix < 2 || radix > 36) throw new ArgumentOutOfRangeException("radix"); if (Sign == 0) return "0"; var negative = Sign < 0; var a = new xIntX(this, DataBitWidth, _unsigned); if (negative) try { a = -a; } catch (Exception ex) { } var biRadix = new xIntX(radix, DataBitWidth, _unsigned); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; var al = new ArrayList(); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); al.Insert(0, charSet[(int)remainder.Data[0]]); a = quotient; } catch (Exception ex) { break; } var result = new string((char[])al.ToArray(typeof(char))); if (radix == 10 && negative) return "-" + result; return result; } public static xIntX Abs(xIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.Sign < 0) return -value; return value; } 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 xIntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static xIntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static xIntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static xIntX 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 xIntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out xIntX result) { result = 0; if (string.IsNullOrEmpty(value)) return false; if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(1); } else { if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(2); } } if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier) return TryParseNum(value, 16, out result); return TryParseNum(value, 10, out result); } public static bool TryParseNum(string digits, int radix, out xIntX result) { result = new xIntX(0, DataBitWidth, _unsigned); if (digits == null) return false; var multiplier = new xIntX(1, DataBitWidth * 2, _unsigned); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int)digits[idx]; if (d != 48) { var a = 1; } if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else return false; if (d >= radix) return false; result += multiplier * d; multiplier *= radix; if (multiplier.DataUsed > DataLength) throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} "); } if (digits[0] == '-' && !_unsigned) result = -result; return true; } public static int Compare(xIntX left, object right) { if (right is xIntX) return Compare(left, (xIntX)right); if (right is bool) return Compare(left, new xIntX((bool)right, DataBitWidth, _unsigned)); if (right is byte) return Compare(left, new xIntX((byte)right, DataBitWidth, _unsigned)); if (right is char) return Compare(left, new xIntX((char)right, DataBitWidth, _unsigned)); if (right is decimal) return Compare(left, new xIntX((decimal)right, DataBitWidth, _unsigned)); if (right is double) return Compare(left, new xIntX((double)right, DataBitWidth, _unsigned)); if (right is short) return Compare(left, new xIntX((short)right, DataBitWidth, _unsigned)); if (right is int) return Compare(left, new xIntX((int)right, DataBitWidth, _unsigned)); if (right is long) return Compare(left, new xIntX((long)right, DataBitWidth, _unsigned)); if (right is sbyte) return Compare(left, new xIntX((sbyte)right, DataBitWidth, _unsigned)); if (right is float) return Compare(left, new xIntX((float)right, DataBitWidth, _unsigned)); if (right is ushort) return Compare(left, new xIntX((ushort)right, DataBitWidth, _unsigned)); if (right is uint) return Compare(left, new xIntX((uint)right, DataBitWidth, _unsigned)); if (right is ulong) return Compare(left, new xIntX((ulong)right, DataBitWidth, _unsigned)); var bytes = right as byte[]; if (bytes != null) return Compare(left, new xIntX(bytes, DataBitWidth, _unsigned)); if (right is Guid) return Compare(left, new xIntX((Guid)right, DataBitWidth, _unsigned)); throw new ArgumentException(); } public static int Compare(xIntX left, xIntX right) { if (ReferenceEquals(left, right)) return 0; if (left.Sign >= 0 && right.Sign < 0) return 1; if (left.Sign < 0 && right.Sign >= 0) return -1; if (left.Data.Length != right.Data.Length) return -1; for (var i = left.Data.Length - 1; i > 0; i--) if (left.Data[i] != right.Data[i]) return left.Data[i].CompareTo(right.Data[i]); return left.Data[0].CompareTo(right.Data[0]); } public static implicit operator xIntX(bool value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(byte value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(char value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(decimal value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(double value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(short value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(int value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(long value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(sbyte value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator xIntX(float value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(ushort value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(uint value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(ulong value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigInteger value) { if (DataBitWidth == 0) DataBitWidth = value.GetBitWidth(); return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigRational value) { return new xIntX(value, DataBitWidth, _unsigned); } public static implicit operator xIntX(BigDecimal value) { return new xIntX(value, DataBitWidth, _unsigned); } public static explicit operator bool(xIntX value) { return (byte)value.Data[0] != 0; } public static explicit operator byte(xIntX value) { return (byte)value.Data[0]; } public static explicit operator char(xIntX value) { return (char)(ushort)value.Data[0]; } public static explicit operator decimal(xIntX value) { if (value.Sign == 0) return 0; if (value.Data.Length == 1) return new decimal((int)value.Data[0], 0, 0, value.Sign < 0, 0); if (value.Data.Length == 2) return new decimal((int)value.Data[0], (int)value.Data[1], 0, value.Sign < 0, 0); if (value.Data.Length == 3) return new decimal((int)value.Data[0], (int)value.Data[1], (int)value.Data[2], value.Sign < 0, 0); throw new ArgumentException("Value length exceeds decimal length."); } public static explicit operator double(xIntX value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d)) throw new OverflowException(); return d; } public static explicit operator float(xIntX value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f)) throw new OverflowException(); return f; } public static explicit operator short(xIntX value) { if (value.Data[0] > 0x8000) throw new OverflowException(); if (value.Data[0] == 0x8000 && value.Sign > 0) throw new OverflowException(); return (short)((int)value.Data[0] * value.Sign); } public static explicit operator int(xIntX value) { if (value.Sign == 0) return 0; return (int)value.Data[0] * value.Sign; } public static explicit operator long(xIntX value) { if (value.Sign == 0) return 0; if (value.Data[0] > int.MaxValue) throw new OverflowException(); if (value.Data.Length > 1) if (value.Data[1] != 0) return (long)(((ulong)value.Data[1] << 32) | value.Data[0]) * value.Sign; return value.Data[0] * value.Sign; } public static explicit operator uint(xIntX value) { if (value.Sign == 0) return 0; return value.Data[0]; } public static explicit operator ushort(xIntX value) { if (value.Sign == 0) return 0; return (ushort)value.Data[0]; } public static explicit operator ulong(xIntX value) { if (value.Data.Length > 1) if (value.Data[1] != 0) return ((ulong)value.Data[1] << 32) | value.Data[0]; return value.Data[0]; } public static explicit operator BigInteger(xIntX value) { return new BigInteger(value.ToByteArray()); } public static explicit operator BigDecimal(xIntX value) { return new xIntX(value.ToByteArray()); } public static explicit operator BigRational(xIntX value) { return new BigRational(new BigInteger(value.ToByteArray())); } public static bool operator >(xIntX left, xIntX right) { return left.CompareTo(right) > 0; } public static bool operator <(xIntX left, xIntX right) { return Compare(left, right) < 0; } public static bool operator >=(xIntX left, xIntX right) { return Compare(left, right) >= 0; } public static bool operator <=(xIntX left, xIntX right) { return Compare(left, right) <= 0; } public static bool operator !=(xIntX left, xIntX right) { return !left.Equals(right); } public static bool operator ==(xIntX left, xIntX right) { return left.Equals(right); } public static xIntX operator +(xIntX value) { return value; } public static xIntX operator ~(xIntX value) { var da = new uint[DataLength]; for (var idx = 0; idx < DataLength; idx++) da[idx] = ~value.Data[idx]; return new xIntX(da, DataBitWidth); } public static xIntX operator -(xIntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.IsZero) return 0; var da = new uint[DataLength]; for (var i = 0; i < da.Length; i++) da[i] = ~value.Data[i]; var carry = true; var index = 0; while (carry && index < da.Length) { var val = (long)da[index] + 1; da[index] = (uint)(val & AllBits); carry = val >> DataSizeBits > 0; index++; } return new xIntX(da, DataBitWidth); } public static xIntX operator ++(xIntX value) { return value + 1; } public static xIntX operator --(xIntX value) { return value - 1; } public static xIntX Negate(xIntX value) { var ldata = (uint[])value.Data.Clone(); for (var i = 0; i < value.Data.Length; i++) ldata[i] = ~value.Data[i]; return new xIntX(value.Sign, ldata, DataBitWidth, _unsigned); } public static xIntX operator +(xIntX left, xIntX right) { if (right.IsZero) return left; if (left.IsZero) return right; var dl = Math.Max(left.Data.Length, right.Data.Length); var lim = Math.Min(left.Data.Length, right.Data.Length); var result = new uint[dl]; long carry = 0; for (var i = 0; i < dl && i < lim; i++) { var sum = left.Data[i] + (long)right.Data[i] + carry; carry = sum >> 32; result[i] = (uint)(sum & 0xFFFFFFFF); } if (carry != 0) { var idx = 0; while (idx < result.Length - 1) { if (result[idx] == 0) break; idx++; } result[idx] = (uint)carry; } return new xIntX(left.Sign * right.Sign, result, DataBitWidth, _unsigned); } public static xIntX operator -(xIntX left, xIntX right) { if (right.IsZero) return left; if (left.IsZero) return -right; var size = Math.Max(left.Data.Length, right.Data.Length) + 1; var da = new uint[size]; long carry = 0; for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++) { var diff = left.Data[i] - (long)right.Data[i] - carry; da[i] = (uint)(diff & AllBits); carry = diff < 0 ? 1 : 0; } return new xIntX(da, DataBitWidth); } public static xIntX Add(xIntX left, xIntX right) { return left + right; } public static xIntX Subtract(xIntX left, xIntX right) { return left - right; } public static xIntX Divide(xIntX dividend, xIntX divisor) { if (divisor == 0) throw new DivideByZeroException(); return DivRem(dividend, divisor, out var integer); } public static void Divide(xIntX dividend, xIntX divisor, out xIntX remainder, out xIntX quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quo, out var rem); remainder = new xIntX(1, rem, DataBitWidth, _unsigned); quotient = new xIntX(dividend.Sign * divisor.Sign, quo, DataBitWidth, _unsigned); } public static xIntX DivRem(xIntX dividend, xIntX divisor, out xIntX remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quotient, out var rem); remainder = new xIntX(1, rem, DataBitWidth, _unsigned); return new xIntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth, _unsigned); } private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder) { const ulong hiBit = 0x100000000; var divisorLen = GetLength(divisor); var dividendLen = GetLength(dividend); if (divisorLen <= 1) { ulong rem = 0; var div = divisor[0]; quotient = new uint[dividendLen]; remainder = new uint[1]; for (var i = dividendLen - 1; i >= 0; i--) { rem *= hiBit; rem += dividend[i]; var q = rem / div; rem -= q * div; quotient[i] = (uint)q; } remainder[0] = (uint)rem; return; } if (dividendLen >= divisorLen) { var shift = GetNormalizeShift(divisor[divisorLen - 1]); var normDividend = new uint[dividendLen + 1]; var normDivisor = new uint[divisorLen]; Normalize(dividend, dividendLen, normDividend, shift); Normalize(divisor, divisorLen, normDivisor, shift); quotient = new uint[dividendLen - divisorLen + 1]; for (var j = dividendLen - divisorLen; j >= 0; j--) { var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1]; var qj = dx / normDivisor[divisorLen - 1]; dx -= qj * normDivisor[divisorLen - 1]; do { if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2]) break; qj -= 1L; dx += normDivisor[divisorLen - 1]; } while (dx < hiBit); ulong di = 0; ulong dj; var index = 0; while (index < divisorLen) { var dqj = normDivisor[index] * qj; dj = normDividend[index + j] - (uint)dqj - di; normDividend[index + j] = (uint)dj; dqj = dqj >> 32; dj = dj >> 32; di = dqj - dj; index++; } dj = normDividend[j + divisorLen] - di; normDividend[j + divisorLen] = (uint)dj; quotient[j] = (uint)qj; if ((long)dj < 0) { quotient[j]--; ulong sum = 0; for (index = 0; index < divisorLen; index++) { sum = normDivisor[index] + normDividend[j + index] + sum; normDividend[j + index] = (uint)sum; sum = sum >> 32; } sum += normDividend[j + divisorLen]; normDividend[j + divisorLen] = (uint)sum; } } remainder = Unnormalize(normDividend, shift); return; } quotient = new uint[1]; remainder = dividend; } private static int GetLength(uint[] uints) { var index = uints.Length - 1; while (index >= 0 && uints[index] == 0) index--; return index + 1; } private static int GetNormalizeShift(uint ui) { var shift = 0; if ((ui & 0xffff0000) == 0) { ui = ui << 16; shift += 16; } if ((ui & 0xff000000) == 0) { ui = ui << 8; shift += 8; } if ((ui & 0xf0000000) == 0) { ui = ui << 4; shift += 4; } if ((ui & 0xc0000000) == 0) { ui = ui << 2; shift += 2; } if ((ui & 0x80000000) == 0) shift++; return shift; } private static uint[] Unnormalize(uint[] normalized, int shift) { var len = GetLength(normalized); var unnormalized = new uint[len]; if (shift > 0) { var rshift = 32 - shift; uint r = 0; for (var i = len - 1; i >= 0; i--) { unnormalized[i] = (normalized[i] >> shift) | r; r = normalized[i] << rshift; } } else { for (var j = 0; j < len; j++) unnormalized[j] = normalized[j]; } return unnormalized; } private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift) { int i; uint n = 0; if (shift > 0) { var rShift = 32 - shift; for (i = 0; i < len; i++) { normalized[i] = (unormalized[i] << shift) | n; n = unormalized[i] >> rShift; } } else { i = 0; while (i < len) { normalized[i] = unormalized[i]; i++; } } while (i < normalized.Length) normalized[i++] = 0; if (n != 0) normalized[len] = n; } public static xIntX Remainder(xIntX dividend, xIntX divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static xIntX Max(xIntX left, xIntX right) { return left.CompareTo(right) < 0 ? right : left; } public static xIntX Min(xIntX left, xIntX right) { return left.CompareTo(right) <= 0 ? left : right; } public static xIntX operator %(xIntX dividend, xIntX divisor) { return Remainder(dividend, divisor); } public static xIntX operator /(xIntX dividend, xIntX divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { var al = Data.Length >> 1; if (al * 2 != Data.Length) al++; var arr = new ulong[al]; Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2); return arr; } public uint[] ToUIn32Array() { return Data; } public byte[] ToByteArray() { var ba = new byte[Data.Length * DataSize]; Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize); return ba; } private void TrimToMsb() { var dataUsed = Data.Length; while (dataUsed > 1 && Data[dataUsed - 1] == 0) --dataUsed; if (dataUsed != Data.Length) { var tData = new uint[dataUsed]; for (var i = 0; i < dataUsed; i++) tData[i] = Data[i]; Data = (uint[])tData.Clone(); } } public static xIntX Multiply(xIntX left, xIntX right) { if (left == 0 || right == 0) return 0; if (left == 1 && right != 1) return right; if (left != 1 && right == 1) return left; if (left == 1 && right == 1) return 1; var xInts = left.Data; var yInts = right.Data; var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1]; for (var i = 0; i < xInts.Length; i++) { var index = i; ulong remainder = 0; foreach (var yi in yInts) { remainder = remainder + (ulong)xInts[i] * yi + mulInts[index]; mulInts[index++] = (uint)remainder; remainder = remainder >> 32; } while (remainder != 0) { remainder += mulInts[index]; mulInts[index++] = (uint)remainder; remainder = remainder >> 32; } } return new xIntX(left.Sign * right.Sign, mulInts, DataBitWidth, _unsigned); } public static xIntX operator *(xIntX left, xIntX right) { return Multiply(left, right); } public static xIntX operator >> (xIntX value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value << int.MaxValue << 1; if (shift < 0) return value << -shift; var xd = value.Data; var shiftAmount = 32; var invShift = 0; var bufLen = xd.Length; while (bufLen > 1 && xd[bufLen - 1] == 0) bufLen--; for (var count = shift; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; invShift = 32 - shiftAmount; } ulong carry = 0; for (var i = bufLen - 1; i >= 0; i--) { var val = (ulong)xd[i] >> shiftAmount; val |= carry; carry = (ulong)xd[i] << invShift; xd[i] = (uint)val; } } return new xIntX(value.Sign, xd, DataBitWidth, _unsigned); } public static xIntX operator <<(xIntX value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value >> int.MaxValue >> 1; if (shift < 0) return value >> -shift; var digitShift = shift / 32; var smallShift = shift - digitShift * 32; var xd = value.Data; var xl = xd.Length; var zd = new uint[xl + digitShift + 1]; if (smallShift == 0) { for (var index = 0; index < xl; ++index) zd[index + digitShift] = xd[index]; } else { var carryShift = 32 - smallShift; uint carry = 0; int index; for (index = 0; index < xl; ++index) { var rot = xd[index]; zd[index + digitShift] = (rot << smallShift) | carry; carry = rot >> carryShift; } zd[index + digitShift] = carry; } return new xIntX(value.Sign, zd, DataBitWidth, _unsigned); } public static xIntX operator |(xIntX left, xIntX right) { if (left == 0) return right; if (right == 0) return left; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu | yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } public static xIntX operator ^(xIntX left, xIntX right) { var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu ^ yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } public static xIntX operator &(xIntX left, xIntX right) { if (left == 0 || right == 0) return 0; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu & yu; } return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned); } public string ToBinaryString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base2 = new StringBuilder(bytes.Length * 8); var binary = Convert.ToString(bytes[index], 2); if (binary[0] != '0' && Sign == 1) base2.Append('0'); base2.Append(binary); for (index--; index >= 0; index--) base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0')); return base2.ToString(); } public string ToOctalString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8); var rem = bytes.Length % 3; if (rem == 0) rem = 3; var base0 = 0; while (rem != 0) { base0 <<= 8; base0 += bytes[index--]; rem--; } var octal = Convert.ToString(base0, 8); if (octal[0] != '0' && Sign == 1) base8.Append('0'); base8.Append(octal); while (index >= 0) { base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2]; base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0')); index -= 3; } return base8.ToString(); } public static xIntX Pow(xIntX value, xIntX exponent, int bitLength) { if (value == null) throw new ArgumentNullException("Value cannot be null"); if (exponent == null) throw new ArgumentNullException("Exponent cannot be null"); if (exponent < 0) throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative"); var result = new xIntX("1", bitLength, _unsigned); while (exponent != 0) { if ((exponent & 1) != 0) result *= value; exponent >>= 1; value *= value; } return result; } /// <summary> /// Works well, not as good as BigInteger /// </summary> public static BigInteger ModPow(BigInteger n, BigInteger e, BigInteger m) { var n1 = n; var e1 = e; if (e1 == 0) return 1; if (e1 == 1) return n1 % m; if (e1 == 2) return n1 * n1 % m; n1 %= m; BigInteger r = 1; if ((e1 & 1) == 1) r = n1; while (e1 > 1) { e1 >>= 1; n1 = n1 * n1 % m; if ((e1 & 1) == 1) r = r * n1 % m; } return r; } public static int GetSign(uint[] value) { var allZero = true; for (var i = 0; i < value.Length; i++) if (value[i] != 0) { allZero = false; break; } if (allZero) return 0; return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1; } private static int GetDataUsed(uint[] array) { var neg = GetSign(array) < 0; var dataUsed = array.Length; if (!neg) { while (dataUsed > 1 && array[dataUsed - 1] == 0) --dataUsed; if (dataUsed == 0) dataUsed = 1; } return dataUsed; } public int GetDecimalPlaces(xIntX a) { var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new xIntX(10, DataBitWidth, _unsigned); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } private uint[] TwosComplement(uint[] d) { var i = 0; uint v = 0; for (; i < d.Length; i++) { v = ~d[i] + 1; d[i] = v; if (v != 0) { i++; break; } } if (v != 0) { for (; i < d.Length; i++) d[i] = ~d[i]; } else { Array.Resize(ref d, d.Length + 1); d[d.Length - 1] = 1; } return d; } public (xIntX approximateRoot, BigRational realRoot) Sqrt() { var n = (BigRational)this; var r = n.Sqrt(); return (r.WholePart, r); } public xIntX Pow(int e) { var ans = this; if (e == 1) return ans; if (e == 0) return 1; for (var i = 1; i != e; i++) ans *= this; return ans; } public static double Log(xIntX value, double baseValue) { var c = 0.0; var d = 0.5; var dataLength = value.DataUsed; var topBits = 0; var x = value.Data[dataLength - 1]; while (x > 0) { x >>= 1; topBits++; } var bitLength = (dataLength - 1) * 32 + topBits; var bit = (uint)(1 << (topBits - 1)); for (var index = dataLength - 1; index >= 0; --index) { for (; bit != 0U; bit >>= 1) { if (((int)value.Data[index] & (int)bit) != 0) c += d; d *= 0.5; } bit = 2147483648U; } return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue); } public static List<xIntX> GetFactors(xIntX n) { var Factors = new List<xIntX>(); var s = (xIntX)1 << ((int)Math.Ceiling(Log(n, 2)) >> 1); var a = (xIntX)3; while (a < s) { if (n % a == 0) { Factors.Add(a); if (a * a != n) Factors.Add(n / a); } a += 2; } return Factors; } public static xIntX GreatestCommonDivisor(xIntX a, xIntX b) { while (b > 0) { var r = a % b; a = b; b = r; } return a; } public static xIntX LeastCommonMultiple(xIntX a, xIntX b) { return a * b / a.Gcd(b); } public static double Log10(xIntX value) { return Log(value, 10.0); } public static double LogN(xIntX value) { return Log(value, 2.0); } public void ConstructFromArray(byte[] value, int bitLength) { var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } private class xIntXConverter : 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 xIntX(0, DataBitWidth, _unsigned); } 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); } } } public class xIntXComparer : IComparer<xIntX> { public int Compare(xIntX left, xIntX right) { return left.CompareTo(right); } public bool Equals(xIntX left, xIntX right) { if (left == null || right == null) return false; return left.Equals(right); } public int GetHashCode(xIntX obj) { return obj.GetHashCode(); } }
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(xIntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public struct xIntX : IComparable<xIntX>, IComparable, IEquatable<xIntX>, IConvertible, IFormattable
{
    private const  int    DefaultDataBitWidth = 1024;
    private const  int    DataSize            = sizeof(uint);
    private const  uint   AllBits             = ~(uint)0;
    private const  int    DataSizeBits        = sizeof(uint) * 8;
    private const  uint   HiNeg               = (uint)1 << (DataSizeBits - 1);
    private static int    _dataBitWidth;
    private static int    DataLength;
    private static bool   _unsigned;
    public         uint[] Data;
    public xIntX(xIntX value, int bitLength = 0, bool unsigned = false)
    {
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        _unsigned  = unsigned;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        value.Data.CopyTo(Data, 0);
    }
    public xIntX(string value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        if (!TryParse(value, out var result))
            throw new Exception("TryParse Failed.");
        Data = new uint[DataLength];
        result.Data.CopyTo(Data, 0);
    }
    public xIntX(byte value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public xIntX(bool value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = (uint)(value ? 1 : 0);
    }
    public xIntX(char value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public xIntX(BigDecimal value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        var ba = value.WholePart.ToByteArray();
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        var len = ba.Length / DataSize;
        var lim = Math.Min(len, DataLength);
        Data = new uint[lim];
        for (var i = 0; i < lim; i++)
            Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
    }
    public xIntX(BigRational value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        var v1 = value.Numerator.ToByteArray();
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        var byteCount      = v1.Length;
        var isNegative     = _unsigned == false && byteCount > 0 && (v1[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  v1[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  v1[curByte];
            }
        }
        ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
    }
    public xIntX(decimal value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        if (value < 0 && !_unsigned)
        {
            var n = -new xIntX(-value, DataBitWidth, _unsigned);
            n.Data.CopyTo(Data, 0);
            return;
        }
        var bits = decimal.GetBits(value);
        Data[2] = (uint)bits[2];
        Data[1] = (uint)bits[1];
        Data[0] = (uint)bits[0];
    }
    public xIntX(double value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned)
    {
    }
    public xIntX(float value, int bitLength = 0, bool unsigned = false) : this((decimal)value, bitLength, unsigned)
    {
    }
    public xIntX(short value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        if (value < 0 && !_unsigned)
        {
            var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint)value;
    }
    public xIntX(int value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        if (value < 0 && !_unsigned)
        {
            var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint)value;
    }
    public xIntX(long value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        if (value < 0 && !_unsigned)
        {
            var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[1] = (uint)((value >> 32) & 0xffffffff);
        Data[0] = (uint)(value         & 0xffffffff);
    }
    public xIntX(sbyte value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        if (value < 0 && !_unsigned)
        {
            var n = -new xIntX(-(value + 1), DataBitWidth, _unsigned) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint)value;
    }
    public xIntX(ushort value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public xIntX(uint value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public xIntX(ulong value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[1]    = (uint)((value >> 32) & 0xffffffff);
        Data[0]    = (uint)(value         & 0xffffffff);
    }
    public xIntX(BigInteger value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned)
    {
    }
    public xIntX(Guid value, int bitLength = 0, bool unsigned = false) : this(value.ToByteArray(), bitLength, unsigned)
    {
    }
    public xIntX(byte[] value, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        var minSize = value.Length / DataSize;
        if (value == null)
            throw new ArgumentNullException("value");
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        var byteCount      = value.Length;
        var isNegative     = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
            }
        }
    }
    public xIntX(int sign, uint[] array, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (array == null)
            throw new Exception("Array cannot be null.");
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Math.Min(DataLength, array.Length); i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
        if (!_unsigned)
        {
            if (sign < 0)
                Data[DataLength - 1] |= HiNeg;
            else
                Data[DataLength - 1] &= ~HiNeg;
        }
    }
    public xIntX(uint[] array, int bitLength = 0, bool unsigned = false)
    {
        _unsigned = unsigned;
        if (array == null)
            throw new Exception("Array cannot be null.");
        if (bitLength == 0)
            DataBitWidth  = DefaultDataBitWidth;
        else DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        if (array.Length != DataLength)
            Array.Resize(ref array, DataLength);
        Data = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Data.Length; i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
    }
    public bool Unsigned
    {
        get => _unsigned;
        set => _unsigned = value;
    }
    private static int DataBitWidth
    {
        get => _dataBitWidth;
        set
        {
            _dataBitWidth = value;
            if (_dataBitWidth < 32)
                _dataBitWidth = 32;
        }
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => ToString();
    public xIntX MaxValue
    {
        get
        {
            var r = new xIntX(0, DataBitWidth, _unsigned);
            for (var i = 0; i < r.Data.Length; ++i)
                r.Data[i] = uint.MaxValue;
            r.Data[r.Data.Length - 1] = int.MaxValue;
            return r;
        }
    }
    public int BitWidth
    {
        get
        {
            xIntX bw = 1;
            var   v  = new xIntX(this, DataBitWidth, _unsigned);
            while ((v >>= 1) > 0)
                bw++;
            if (bw < 8)
                bw = 8;
            while (bw % 8 != 0)
                bw++;
            return (int)bw;
        }
    }
    public float MaxDecimalPlaces => DataBitWidth / 64f * 20f;
    public int DecimalPlaces
    {
        get
        {
            var a       = new xIntX(this, DataBitWidth, _unsigned);
            var dPlaces = 0;
            if (a.Sign == 0)
                return 1;
            if (a.Sign < 0)
                try
                {
                    a = -a;
                }
                catch (Exception ex)
                {
                    return 0;
                }
            var biRadix = new xIntX(10, DataBitWidth, _unsigned);
            while (a > 0)
                try
                {
                    Divide(a, biRadix, out var remainder, out var quotient);
                    a = quotient;
                    dPlaces++;
                }
                catch (Exception ex)
                {
                    break;
                }
            return dPlaces;
        }
    }
    public int Sign
    {
        get
        {
            if (_unsigned)
                return 1;
            var allZero = true;
            var ba      = Data;
            for (var i = 0; i < ba.Length; i++)
                if (ba[i] != 0)
                {
                    allZero = false;
                    break;
                }
            if (allZero)
                return 0;
            return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1;
        }
    }
    public bool IsOne      => this       == 1;
    public bool IsEven     => (this & 1) == 0;
    public bool IsNegative => Sign       < 0;
    public bool IsZero
    {
        get
        {
            for (var i = 0; i < Data.Length; i++)
                if (Data[i] != 0)
                    return false;
            return true;
        }
    }
    public int DataUsed
    {
        get
        {
            var DataUsed = Data.Length;
            if (!IsNegative)
            {
                while (DataUsed > 1 && Data[DataUsed - 1] == 0)
                    --DataUsed;
                if (DataUsed == 0)
                    DataUsed = 1;
            }
            return DataUsed;
        }
    }
    int IComparable.CompareTo(object obj)
    {
        return Compare(this, obj);
    }
    public int CompareTo(xIntX value)
    {
        return Compare(this, value);
    }
    TypeCode IConvertible.GetTypeCode()
    {
        return TypeCode.Object;
    }
    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        return (bool)this;
    }
    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return (byte)this;
    }
    char IConvertible.ToChar(IFormatProvider provider)
    {
        return (char)this;
    }
    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }
    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        return (decimal)this;
    }
    double IConvertible.ToDouble(IFormatProvider provider)
    {
        return (double)this;
    }
    short IConvertible.ToInt16(IFormatProvider provider)
    {
        return (short)this;
    }
    int IConvertible.ToInt32(IFormatProvider provider)
    {
        return (int)this;
    }
    long IConvertible.ToInt64(IFormatProvider provider)
    {
        return (long)this;
    }
    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        return (sbyte)this;
    }
    float IConvertible.ToSingle(IFormatProvider provider)
    {
        return (float)this;
    }
    string IConvertible.ToString(IFormatProvider provider)
    {
        return ToString(null, provider);
    }
    public object ToType(Type conversionType, IFormatProvider provider)
    {
        object value;
        if (TryConvert(conversionType, provider, out value))
            return value;
        throw new InvalidCastException();
    }
    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt16(Data[0]);
    }
    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt32(Data[0]);
    }
    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        if (Data[1] != 0)
            return ((ulong)Data[1] << 32) | Data[0];
        return Data[0];
    }
    public bool Equals(xIntX obj)
    {
        if (ReferenceEquals(obj, null))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (Data.Length != obj.Data.Length)
            return false;
        if (Sign != obj.Sign)
            return false;
        for (var i = 0; i < Data.Length; i++)
            if (Data[i] != obj.Data[i])
                return false;
        return true;
    }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (formatProvider == null)
            formatProvider = CultureInfo.CurrentCulture;
        if (!string.IsNullOrEmpty(format))
        {
            var ch = format[0];
            if (ch == 'x' || ch == 'X')
            {
                int.TryParse(format.Substring(1).Trim(), out var min);
                return ToHexString(ch == 'X');
            }
            if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
                throw new NotSupportedException("Not supported format: " + format);
        }
        return ToString((NumberFormatInfo)formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
    }
    private static byte[] ToByteArray(ulong[] value)
    {
        var ba = new byte[value.Length << 3];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
        return ba;
    }
    private static byte[] ToByteArray(uint[] value)
    {
        var ba = new byte[value.Length << 2];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
        return ba;
    }
    public override int GetHashCode()
    {
        static uint CombineHash(uint u1, uint u2)
        {
            return ((u1 << 7) | (u1 >> 25)) ^ u2;
        }
        var s = Sign;
        var i = Data.Length;
        while (--i >= 0)
            s = (int)CombineHash((uint)s, Data[i]);
        return s;
    }
    public static byte[] GetBytesInt(xIntX value)
    {
        var b  = value.Sign.GetBytes();
        var tb = b.Add(value.Data.GetBytes());
        return tb;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override string ToString()
    {
        return ToString(null, null);
    }
    public string ToString(string format)
    {
        return ToString(format, null);
    }
    public string ToHexString(bool caps)
    {
        var bytes = ToByteArray().Invert();
        var sb    = new StringBuilder();
        var x     = caps ? "X" : "x";
        foreach (var b in bytes)
        {
            var hex = b.ToString($"{x}2");
            sb.Append(hex);
        }
        return sb.ToString();
    }
    private string ToString(NumberFormatInfo info, int radix)
    {
        if (radix < 2 || radix > 36)
            throw new ArgumentOutOfRangeException("radix");
        if (Sign == 0)
            return "0";
        var negative = Sign < 0;
        var a        = new xIntX(this, DataBitWidth, _unsigned);
        if (negative)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
            }
        var          biRadix = new xIntX(radix, DataBitWidth, _unsigned);
        const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
        var          al      = new ArrayList();
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                al.Insert(0, charSet[(int)remainder.Data[0]]);
                a = quotient;
            }
            catch (Exception ex)
            {
                break;
            }
        var result = new string((char[])al.ToArray(typeof(char)));
        if (radix == 10 && negative)
            return "-" + result;
        return result;
    }
    public static xIntX Abs(xIntX value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.Sign < 0)
            return -value;
        return value;
    }
    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 xIntX Parse(string value)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
    }
    public static xIntX Parse(string value, NumberStyles style)
    {
        return Parse(value, style, NumberFormatInfo.CurrentInfo);
    }
    public static xIntX Parse(string value, IFormatProvider provider)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
    }
    public static xIntX 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 xIntX result)
    {
        return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
    }
    public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out xIntX result)
    {
        result = 0;
        if (string.IsNullOrEmpty(value))
            return false;
        if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
        {
            style |= NumberStyles.AllowHexSpecifier;
            value =  value.Substring(1);
        }
        else
        {
            if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
            {
                style |= NumberStyles.AllowHexSpecifier;
                value =  value.Substring(2);
            }
        }
        if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
            return TryParseNum(value, 16, out result);
        return TryParseNum(value, 10, out result);
    }
    public static bool TryParseNum(string digits, int radix, out xIntX result)
    {
        result = new xIntX(0, DataBitWidth, _unsigned);
        if (digits == null)
            return false;
        var multiplier = new xIntX(1, DataBitWidth * 2, _unsigned);
        digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
        var nDigits = digits[0] == '-' ? 1 : 0;
        for (var idx = digits.Length - 1; idx >= nDigits; idx--)
        {
            var d = (int)digits[idx];
            if (d != 48)
            {
                var a = 1;
            }
            if (d >= '0' && d <= '9')
                d -= '0';
            else if (d >= 'A' && d <= 'Z')
                d = d - 'A' + 10;
            else
                return false;
            if (d >= radix)
                return false;
            result     += multiplier * d;
            multiplier *= radix;
            if (multiplier.DataUsed > DataLength)
                throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
        }
        if (digits[0] == '-' && !_unsigned)
            result = -result;
        return true;
    }
    public static int Compare(xIntX left, object right)
    {
        if (right is xIntX)
            return Compare(left, (xIntX)right);
        if (right is bool)
            return Compare(left, new xIntX((bool)right, DataBitWidth, _unsigned));
        if (right is byte)
            return Compare(left, new xIntX((byte)right, DataBitWidth, _unsigned));
        if (right is char)
            return Compare(left, new xIntX((char)right, DataBitWidth, _unsigned));
        if (right is decimal)
            return Compare(left, new xIntX((decimal)right, DataBitWidth, _unsigned));
        if (right is double)
            return Compare(left, new xIntX((double)right, DataBitWidth, _unsigned));
        if (right is short)
            return Compare(left, new xIntX((short)right, DataBitWidth, _unsigned));
        if (right is int)
            return Compare(left, new xIntX((int)right, DataBitWidth, _unsigned));
        if (right is long)
            return Compare(left, new xIntX((long)right, DataBitWidth, _unsigned));
        if (right is sbyte)
            return Compare(left, new xIntX((sbyte)right, DataBitWidth, _unsigned));
        if (right is float)
            return Compare(left, new xIntX((float)right, DataBitWidth, _unsigned));
        if (right is ushort)
            return Compare(left, new xIntX((ushort)right, DataBitWidth, _unsigned));
        if (right is uint)
            return Compare(left, new xIntX((uint)right, DataBitWidth, _unsigned));
        if (right is ulong)
            return Compare(left, new xIntX((ulong)right, DataBitWidth, _unsigned));
        var bytes = right as byte[];
        if (bytes != null)
            return Compare(left, new xIntX(bytes, DataBitWidth, _unsigned));
        if (right is Guid)
            return Compare(left, new xIntX((Guid)right, DataBitWidth, _unsigned));
        throw new ArgumentException();
    }
    public static int Compare(xIntX left, xIntX right)
    {
        if (ReferenceEquals(left, right))
            return 0;
        if (left.Sign >= 0 && right.Sign < 0)
            return 1;
        if (left.Sign < 0 && right.Sign >= 0)
            return -1;
        if (left.Data.Length != right.Data.Length)
            return -1;
        for (var i = left.Data.Length - 1; i > 0; i--)
            if (left.Data[i] != right.Data[i])
                return left.Data[i].CompareTo(right.Data[i]);
        return left.Data[0].CompareTo(right.Data[0]);
    }
    public static implicit operator xIntX(bool value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(byte value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(char value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static explicit operator xIntX(decimal value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static explicit operator xIntX(double value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(short value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(int value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(long value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(sbyte value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static explicit operator xIntX(float value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(ushort value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(uint value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(ulong value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(BigInteger value)
    {
        if (DataBitWidth == 0)
            DataBitWidth = value.GetBitWidth();
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(BigRational value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static implicit operator xIntX(BigDecimal value)
    {
        return new xIntX(value, DataBitWidth, _unsigned);
    }
    public static explicit operator bool(xIntX value)
    {
        return (byte)value.Data[0] != 0;
    }
    public static explicit operator byte(xIntX value)
    {
        return (byte)value.Data[0];
    }
    public static explicit operator char(xIntX value)
    {
        return (char)(ushort)value.Data[0];
    }
    public static explicit operator decimal(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data.Length == 1)
            return new decimal((int)value.Data[0], 0, 0, value.Sign < 0, 0);
        if (value.Data.Length == 2)
            return new decimal((int)value.Data[0], (int)value.Data[1], 0, value.Sign < 0, 0);
        if (value.Data.Length == 3)
            return new decimal((int)value.Data[0], (int)value.Data[1], (int)value.Data[2], value.Sign < 0, 0);
        throw new ArgumentException("Value length exceeds decimal length.");
    }
    public static explicit operator double(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
            throw new OverflowException();
        return d;
    }
    public static explicit operator float(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
            throw new OverflowException();
        return f;
    }
    public static explicit operator short(xIntX value)
    {
        if (value.Data[0] > 0x8000)
            throw new OverflowException();
        if (value.Data[0] == 0x8000 && value.Sign > 0)
            throw new OverflowException();
        return (short)((int)value.Data[0] * value.Sign);
    }
    public static explicit operator int(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (int)value.Data[0] * value.Sign;
    }
    public static explicit operator long(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data[0] > int.MaxValue)
            throw new OverflowException();
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return (long)(((ulong)value.Data[1] << 32) | value.Data[0]) * value.Sign;
        return value.Data[0] * value.Sign;
    }
    public static explicit operator uint(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        return value.Data[0];
    }
    public static explicit operator ushort(xIntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (ushort)value.Data[0];
    }
    public static explicit operator ulong(xIntX value)
    {
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return ((ulong)value.Data[1] << 32) | value.Data[0];
        return value.Data[0];
    }
    public static explicit operator BigInteger(xIntX value)
    {
        return new BigInteger(value.ToByteArray());
    }
    public static explicit operator BigDecimal(xIntX value)
    {
        return new xIntX(value.ToByteArray());
    }
    public static explicit operator BigRational(xIntX value)
    {
        return new BigRational(new BigInteger(value.ToByteArray()));
    }
    public static bool operator >(xIntX left, xIntX right)
    {
        return left.CompareTo(right) > 0;
    }
    public static bool operator <(xIntX left, xIntX right)
    {
        return Compare(left, right) < 0;
    }
    public static bool operator >=(xIntX left, xIntX right)
    {
        return Compare(left, right) >= 0;
    }
    public static bool operator <=(xIntX left, xIntX right)
    {
        return Compare(left, right) <= 0;
    }
    public static bool operator !=(xIntX left, xIntX right)
    {
        return !left.Equals(right);
    }
    public static bool operator ==(xIntX left, xIntX right)
    {
        return left.Equals(right);
    }
    public static xIntX operator +(xIntX value)
    {
        return value;
    }
    public static xIntX operator ~(xIntX value)
    {
        var da = new uint[DataLength];
        for (var idx = 0; idx < DataLength; idx++)
            da[idx] = ~value.Data[idx];
        return new xIntX(da, DataBitWidth);
    }
    public static xIntX operator -(xIntX value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.IsZero)
            return 0;
        var da = new uint[DataLength];
        for (var i = 0; i < da.Length; i++)
            da[i] = ~value.Data[i];
        var carry = true;
        var index = 0;
        while (carry && index < da.Length)
        {
            var val = (long)da[index] + 1;
            da[index] = (uint)(val & AllBits);
            carry     = val >> DataSizeBits > 0;
            index++;
        }
        return new xIntX(da, DataBitWidth);
    }
    public static xIntX operator ++(xIntX value)
    {
        return value + 1;
    }
    public static xIntX operator --(xIntX value)
    {
        return value - 1;
    }
    public static xIntX Negate(xIntX value)
    {
        var ldata = (uint[])value.Data.Clone();
        for (var i = 0; i < value.Data.Length; i++)
            ldata[i] = ~value.Data[i];
        return new xIntX(value.Sign, ldata, DataBitWidth, _unsigned);
    }
    public static xIntX operator +(xIntX left, xIntX right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return right;
        var  dl     = Math.Max(left.Data.Length, right.Data.Length);
        var  lim    = Math.Min(left.Data.Length, right.Data.Length);
        var  result = new uint[dl];
        long carry  = 0;
        for (var i = 0; i < dl && i < lim; i++)
        {
            var sum = left.Data[i] + (long)right.Data[i] + carry;
            carry     = sum >> 32;
            result[i] = (uint)(sum & 0xFFFFFFFF);
        }
        if (carry != 0)
        {
            var idx = 0;
            while (idx < result.Length - 1)
            {
                if (result[idx] == 0)
                    break;
                idx++;
            }
            result[idx] = (uint)carry;
        }
        return new xIntX(left.Sign * right.Sign, result, DataBitWidth, _unsigned);
    }
    public static xIntX operator -(xIntX left, xIntX right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return -right;
        var  size  = Math.Max(left.Data.Length, right.Data.Length) + 1;
        var  da    = new uint[size];
        long carry = 0;
        for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++)
        {
            var diff = left.Data[i] - (long)right.Data[i] - carry;
            da[i] = (uint)(diff & AllBits);
            carry = diff < 0 ? 1 : 0;
        }
        return new xIntX(da, DataBitWidth);
    }
    public static xIntX Add(xIntX left, xIntX right)
    {
        return left + right;
    }
    public static xIntX Subtract(xIntX left, xIntX right)
    {
        return left - right;
    }
    public static xIntX Divide(xIntX dividend, xIntX divisor)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        return DivRem(dividend, divisor, out var integer);
    }
    public static void Divide(xIntX dividend, xIntX divisor, out xIntX remainder, out xIntX quotient)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
        remainder = new xIntX(1,                            rem, DataBitWidth, _unsigned);
        quotient  = new xIntX(dividend.Sign * divisor.Sign, quo, DataBitWidth, _unsigned);
    }
    public static xIntX DivRem(xIntX dividend, xIntX divisor, out xIntX remainder)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
        remainder = new xIntX(1, rem, DataBitWidth, _unsigned);
        return new xIntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth, _unsigned);
    }
    private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
    {
        const ulong hiBit       = 0x100000000;
        var         divisorLen  = GetLength(divisor);
        var         dividendLen = GetLength(dividend);
        if (divisorLen <= 1)
        {
            ulong rem = 0;
            var   div = divisor[0];
            quotient  = new uint[dividendLen];
            remainder = new uint[1];
            for (var i = dividendLen - 1; i >= 0; i--)
            {
                rem *= hiBit;
                rem += dividend[i];
                var q = rem / div;
                rem         -= q * div;
                quotient[i] =  (uint)q;
            }
            remainder[0] = (uint)rem;
            return;
        }
        if (dividendLen >= divisorLen)
        {
            var shift        = GetNormalizeShift(divisor[divisorLen - 1]);
            var normDividend = new uint[dividendLen + 1];
            var normDivisor  = new uint[divisorLen];
            Normalize(dividend, dividendLen, normDividend, shift);
            Normalize(divisor,  divisorLen,  normDivisor,  shift);
            quotient = new uint[dividendLen - divisorLen + 1];
            for (var j = dividendLen - divisorLen; j >= 0; j--)
            {
                var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
                var qj = dx / normDivisor[divisorLen                                        - 1];
                dx -= qj * normDivisor[divisorLen - 1];
                do
                {
                    if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
                        break;
                    qj -= 1L;
                    dx += normDivisor[divisorLen - 1];
                } while (dx < hiBit);
                ulong di = 0;
                ulong dj;
                var   index = 0;
                while (index < divisorLen)
                {
                    var dqj = normDivisor[index] * qj;
                    dj                      = normDividend[index + j] - (uint)dqj - di;
                    normDividend[index + j] = (uint)dj;
                    dqj                     = dqj >> 32;
                    dj                      = dj  >> 32;
                    di                      = dqj - dj;
                    index++;
                }
                dj                           = normDividend[j + divisorLen] - di;
                normDividend[j + divisorLen] = (uint)dj;
                quotient[j]                  = (uint)qj;
                if ((long)dj < 0)
                {
                    quotient[j]--;
                    ulong sum = 0;
                    for (index = 0; index < divisorLen; index++)
                    {
                        sum                     = normDivisor[index] + normDividend[j + index] + sum;
                        normDividend[j + index] = (uint)sum;
                        sum                     = sum >> 32;
                    }
                    sum += normDividend[j + divisorLen];
                    normDividend[j        + divisorLen] = (uint)sum;
                }
            }
            remainder = Unnormalize(normDividend, shift);
            return;
        }
        quotient  = new uint[1];
        remainder = dividend;
    }
    private static int GetLength(uint[] uints)
    {
        var index = uints.Length - 1;
        while (index >= 0 && uints[index] == 0)
            index--;
        return index + 1;
    }
    private static int GetNormalizeShift(uint ui)
    {
        var shift = 0;
        if ((ui & 0xffff0000) == 0)
        {
            ui    =  ui << 16;
            shift += 16;
        }
        if ((ui & 0xff000000) == 0)
        {
            ui    =  ui << 8;
            shift += 8;
        }
        if ((ui & 0xf0000000) == 0)
        {
            ui    =  ui << 4;
            shift += 4;
        }
        if ((ui & 0xc0000000) == 0)
        {
            ui    =  ui << 2;
            shift += 2;
        }
        if ((ui & 0x80000000) == 0)
            shift++;
        return shift;
    }
    private static uint[] Unnormalize(uint[] normalized, int shift)
    {
        var len          = GetLength(normalized);
        var unnormalized = new uint[len];
        if (shift > 0)
        {
            var  rshift = 32 - shift;
            uint r      = 0;
            for (var i = len - 1; i >= 0; i--)
            {
                unnormalized[i] = (normalized[i] >> shift) | r;
                r               = normalized[i] << rshift;
            }
        }
        else
        {
            for (var j = 0; j < len; j++)
                unnormalized[j] = normalized[j];
        }
        return unnormalized;
    }
    private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
    {
        int  i;
        uint n = 0;
        if (shift > 0)
        {
            var rShift = 32 - shift;
            for (i = 0; i < len; i++)
            {
                normalized[i] = (unormalized[i] << shift) | n;
                n             = unormalized[i] >> rShift;
            }
        }
        else
        {
            i = 0;
            while (i < len)
            {
                normalized[i] = unormalized[i];
                i++;
            }
        }
        while (i < normalized.Length)
            normalized[i++] = 0;
        if (n != 0)
            normalized[len] = n;
    }
    public static xIntX Remainder(xIntX dividend, xIntX divisor)
    {
        DivRem(dividend, divisor, out var remainder);
        return remainder;
    }
    public static xIntX Max(xIntX left, xIntX right)
    {
        return left.CompareTo(right) < 0 ? right : left;
    }
    public static xIntX Min(xIntX left, xIntX right)
    {
        return left.CompareTo(right) <= 0 ? left : right;
    }
    public static xIntX operator %(xIntX dividend, xIntX divisor)
    {
        return Remainder(dividend, divisor);
    }
    public static xIntX operator /(xIntX dividend, xIntX divisor)
    {
        return Divide(dividend, divisor);
    }
    public ulong[] ToUIn64Array()
    {
        var al = Data.Length >> 1;
        if (al * 2 != Data.Length)
            al++;
        var arr = new ulong[al];
        Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2);
        return arr;
    }
    public uint[] ToUIn32Array()
    {
        return Data;
    }
    public byte[] ToByteArray()
    {
        var ba = new byte[Data.Length * DataSize];
        Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize);
        return ba;
    }
    private void TrimToMsb()
    {
        var dataUsed = Data.Length;
        while (dataUsed > 1 && Data[dataUsed - 1] == 0)
            --dataUsed;
        if (dataUsed != Data.Length)
        {
            var tData = new uint[dataUsed];
            for (var i = 0; i < dataUsed; i++)
                tData[i] = Data[i];
            Data = (uint[])tData.Clone();
        }
    }
    public static xIntX Multiply(xIntX left, xIntX right)
    {
        if (left == 0 || right == 0)
            return 0;
        if (left == 1 && right != 1)
            return right;
        if (left != 1 && right == 1)
            return left;
        if (left == 1 && right == 1)
            return 1;
        var xInts   = left.Data;
        var yInts   = right.Data;
        var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
        for (var i = 0; i < xInts.Length; i++)
        {
            var   index     = i;
            ulong remainder = 0;
            foreach (var yi in yInts)
            {
                remainder        = remainder + (ulong)xInts[i] * yi + mulInts[index];
                mulInts[index++] = (uint)remainder;
                remainder        = remainder >> 32;
            }
            while (remainder != 0)
            {
                remainder        += mulInts[index];
                mulInts[index++] =  (uint)remainder;
                remainder        =  remainder >> 32;
            }
        }
        return new xIntX(left.Sign * right.Sign, mulInts, DataBitWidth, _unsigned);
    }
    public static xIntX operator *(xIntX left, xIntX right)
    {
        return Multiply(left, right);
    }
    public static xIntX operator >> (xIntX value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value << int.MaxValue << 1;
        if (shift < 0)
            return value << -shift;
        var xd          = value.Data;
        var shiftAmount = 32;
        var invShift    = 0;
        var bufLen      = xd.Length;
        while (bufLen > 1 && xd[bufLen - 1] == 0)
            bufLen--;
        for (var count = shift; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
            {
                shiftAmount = count;
                invShift    = 32 - shiftAmount;
            }
            ulong carry = 0;
            for (var i = bufLen - 1; i >= 0; i--)
            {
                var val = (ulong)xd[i] >> shiftAmount;
                val   |= carry;
                carry =  (ulong)xd[i] << invShift;
                xd[i] =  (uint)val;
            }
        }
        return new xIntX(value.Sign, xd, DataBitWidth, _unsigned);
    }
    public static xIntX operator <<(xIntX value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value >> int.MaxValue >> 1;
        if (shift < 0)
            return value >> -shift;
        var digitShift = shift / 32;
        var smallShift = shift - digitShift * 32;
        var xd         = value.Data;
        var xl         = xd.Length;
        var zd         = new uint[xl + digitShift + 1];
        if (smallShift == 0)
        {
            for (var index = 0; index < xl; ++index)
                zd[index + digitShift] = xd[index];
        }
        else
        {
            var  carryShift = 32 - smallShift;
            uint carry      = 0;
            int  index;
            for (index = 0; index < xl; ++index)
            {
                var rot = xd[index];
                zd[index + digitShift] = (rot << smallShift) | carry;
                carry                  = rot >> carryShift;
            }
            zd[index + digitShift] = carry;
        }
        return new xIntX(value.Sign, zd, DataBitWidth, _unsigned);
    }
    public static xIntX operator |(xIntX left, xIntX right)
    {
        if (left == 0)
            return right;
        if (right == 0)
            return left;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu | yu;
        }
        return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
    }
    public static xIntX operator ^(xIntX left, xIntX right)
    {
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu ^ yu;
        }
        return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
    }
    public static xIntX operator &(xIntX left, xIntX right)
    {
        if (left == 0 || right == 0)
            return 0;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu & yu;
        }
        return new xIntX(left.Sign * right.Sign, z, DataBitWidth, _unsigned);
    }
    public string ToBinaryString()
    {
        var bytes  = ToByteArray();
        var index  = bytes.Length - 1;
        var base2  = new StringBuilder(bytes.Length * 8);
        var binary = Convert.ToString(bytes[index], 2);
        if (binary[0] != '0' && Sign == 1) base2.Append('0');
        base2.Append(binary);
        for (index--; index >= 0; index--)
            base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
        return base2.ToString();
    }
    public string ToOctalString()
    {
        var bytes         = ToByteArray();
        var index         = bytes.Length - 1;
        var base8         = new StringBuilder((bytes.Length / 3 + 1) * 8);
        var rem           = bytes.Length % 3;
        if (rem == 0) rem = 3;
        var base0         = 0;
        while (rem != 0)
        {
            base0 <<= 8;
            base0 +=  bytes[index--];
            rem--;
        }
        var octal = Convert.ToString(base0, 8);
        if (octal[0] != '0' && Sign == 1) base8.Append('0');
        base8.Append(octal);
        while (index >= 0)
        {
            base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
            base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
            index -= 3;
        }
        return base8.ToString();
    }
    public static xIntX Pow(xIntX value, xIntX exponent, int bitLength)
    {
        if (value == null)
            throw new ArgumentNullException("Value cannot be null");
        if (exponent == null)
            throw new ArgumentNullException("Exponent cannot be null");
        if (exponent < 0)
            throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative");
        var result = new xIntX("1", bitLength, _unsigned);
        while (exponent != 0)
        {
            if ((exponent & 1) != 0)
                result *= value;
            exponent >>= 1;
            value    *=  value;
        }
        return result;
    }
    /// <summary>
    ///     Works well, not as good as BigInteger
    /// </summary>
    public static BigInteger ModPow(BigInteger n, BigInteger e, BigInteger m)
    {
        var n1 = n;
        var e1 = e;
        if (e1 == 0)
            return 1;
        if (e1 == 1)
            return n1 % m;
        if (e1 == 2)
            return n1 * n1 % m;
        n1 %= m;
        BigInteger r = 1;
        if ((e1 & 1) == 1)
            r = n1;
        while (e1 > 1)
        {
            e1 >>= 1;
            n1 =   n1 * n1 % m;
            if ((e1 & 1) == 1)
                r = r * n1 % m;
        }
        return r;
    }
    public static int GetSign(uint[] value)
    {
        var allZero = true;
        for (var i = 0; i < value.Length; i++)
            if (value[i] != 0)
            {
                allZero = false;
                break;
            }
        if (allZero)
            return 0;
        return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1;
    }
    private static int GetDataUsed(uint[] array)
    {
        var neg      = GetSign(array) < 0;
        var dataUsed = array.Length;
        if (!neg)
        {
            while (dataUsed > 1 && array[dataUsed - 1] == 0)
                --dataUsed;
            if (dataUsed == 0)
                dataUsed = 1;
        }
        return dataUsed;
    }
    public int GetDecimalPlaces(xIntX a)
    {
        var dPlaces = 0;
        if (a.Sign == 0)
            return 1;
        if (a.Sign < 0)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
                return 0;
            }
        var biRadix = new xIntX(10, DataBitWidth, _unsigned);
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                a = quotient;
                dPlaces++;
            }
            catch (Exception ex)
            {
                break;
            }
        return dPlaces;
    }
    private uint[] TwosComplement(uint[] d)
    {
        var  i = 0;
        uint v = 0;
        for (; i < d.Length; i++)
        {
            v    = ~d[i] + 1;
            d[i] = v;
            if (v != 0)
            {
                i++;
                break;
            }
        }
        if (v != 0)
        {
            for (; i < d.Length; i++)
                d[i] = ~d[i];
        }
        else
        {
            Array.Resize(ref d, d.Length + 1);
            d[d.Length - 1] = 1;
        }
        return d;
    }
    public (xIntX approximateRoot, BigRational realRoot) Sqrt()
    {
        var n = (BigRational)this;
        var r = n.Sqrt();
        return (r.WholePart, r);
    }
    public xIntX Pow(int e)
    {
        var ans = this;
        if (e == 1)
            return ans;
        if (e == 0)
            return 1;
        for (var i = 1; i != e; i++)
            ans *= this;
        return ans;
    }
    public static double Log(xIntX value, double baseValue)
    {
        var c          = 0.0;
        var d          = 0.5;
        var dataLength = value.DataUsed;
        var topBits    = 0;
        var x          = value.Data[dataLength - 1];
        while (x > 0)
        {
            x >>= 1;
            topBits++;
        }
        var bitLength = (dataLength - 1) * 32 + topBits;
        var bit       = (uint)(1 << (topBits - 1));
        for (var index = dataLength - 1; index >= 0; --index)
        {
            for (; bit != 0U; bit >>= 1)
            {
                if (((int)value.Data[index] & (int)bit) != 0)
                    c += d;
                d *= 0.5;
            }
            bit = 2147483648U;
        }
        return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue);
    }
    public static List<xIntX> GetFactors(xIntX n)
    {
        var Factors = new List<xIntX>();
        var s       = (xIntX)1 << ((int)Math.Ceiling(Log(n, 2)) >> 1);
        var a       = (xIntX)3;
        while (a < s)
        {
            if (n % a == 0)
            {
                Factors.Add(a);
                if (a * a != n)
                    Factors.Add(n / a);
            }
            a += 2;
        }
        return Factors;
    }
    public static xIntX GreatestCommonDivisor(xIntX a, xIntX b)
    {
        while (b > 0)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static xIntX LeastCommonMultiple(xIntX a, xIntX b)
    {
        return a * b / a.Gcd(b);
    }
    public static double Log10(xIntX value)
    {
        return Log(value, 10.0);
    }
    public static double LogN(xIntX value)
    {
        return Log(value, 2.0);
    }
    public void ConstructFromArray(byte[] value, int bitLength)
    {
        var minSize = value.Length / DataSize;
        if (value == null)
            throw new ArgumentNullException("value");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        var byteCount      = value.Length;
        var isNegative     = _unsigned == false && byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
            }
        }
    }
    private class xIntXConverter : 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 xIntX(0, DataBitWidth, _unsigned);
        }
        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);
        }
    }
}
public class xIntXComparer : IComparer<xIntX>
{
    public int Compare(xIntX left, xIntX right)
    {
        return left.CompareTo(right);
    }
    public bool Equals(xIntX left, xIntX right)
    {
        if (left == null || right == null)
            return false;
        return left.Equals(right);
    }
    public int GetHashCode(xIntX obj)
    {
        return obj.GetHashCode();
    }
}

BigRational.cs

C# Arbitrary Precision Signed Big Rational Numbers

Updated: Jun-11,2021

using System;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[DebuggerDisplay("{" + nameof(DDisplay) + "}")]
[Serializable]
public struct BigRational : IComparable, IComparable<BigRational>, IEquatable<BigRational>
{
[StructLayout(LayoutKind.Explicit)]
internal struct DoubleUlong
{
[FieldOffset(0)] public double dbl;
[FieldOffset(0)] public ulong uu;
}
/// <summary>
/// Change here if more then 2048 bits are specified
/// </summary>
private const float DecimalMaxScale = 2048f / 64f * 20f;
private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, (int)DecimalMaxScale);
private const int DoubleMaxScale = 308;
public static BigRational Pi = new(
"3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798162478513934506898440362801792706010179987216806726188740140466033567581311679376075335101609659171030644576233653027450257182803484658351860927270133809030914436823660262931162576284703194589395221866245992710817555393680237554917047871708932985106840785074833639247080859264327721882027979677397953754604196915619381410505600288856897761875941052867609089114345150157869223684881643245943313338421018485091403977277400743970527492816321894223953257584787737337170568053925027217102844351208765657302025589127695185039186644597240030541171074757870137431100579097277905612641495178817964173941740654985445918326928220945355416048444887050935562696866696019631573868714587428709669938320262709342763");
public static BigRational E = GetE(MaxFactorials);
private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, DoubleMaxScale);
private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue;
private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue;
private static BigRational[] Factorials;
static BigRational()
{
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => AsDecimal(this);
[StructLayout(LayoutKind.Explicit)]
internal struct DecimalUInt32
{
[FieldOffset(0)] public decimal dec;
[FieldOffset(0)] public int flags;
}
private const int DecimalScaleMask = 0x00FF0000;
private const int DecimalSignMask = unchecked((int)0x80000000);
private const int MaxFactorials = 100;
private static readonly BigInteger DecimalMaxValue = (BigInteger)decimal.MaxValue;
private static readonly BigInteger DecimalMinValue = (BigInteger)decimal.MinValue;
private const string Solidus = @"/";
public static BigRational Zero
{
get;
} = new(BigInteger.Zero);
public static BigRational One
{
get;
} = new(BigInteger.One);
public static BigRational MinusOne
{
get;
} = new(BigInteger.MinusOne);
public int Sign => Numerator.Sign;
public BigInteger Numerator
{
get;
private set;
}
public BigInteger Denominator
{
get;
private set;
}
public BigInteger WholePart => BigInteger.Divide(Numerator, Denominator);
public bool IsFractionalPart
{
get
{
var fp = FractionPart;
return fp.Numerator != 0 || fp.Denominator != 1;
}
}
public BigInteger GetUnscaledAsDecimal => Numerator * DecimalPrecision / Denominator;
public BigInteger Remainder => Numerator % Denominator;
public int DecimalPlaces
{
get
{
var a = GetUnscaledAsDecimal;
var dPlaces = 0;
if (a.Sign == 0)
return 1;
if (a.Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new BigInteger(10);
while (a > 0)
try
{
a /= biRadix;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
}
public static string AsDecimal(BigRational value)
{
var asd = new BigDecimal(value);
return asd.ToString();
}
public static string CleanAsDecimal(BigRational value)
{
var fpas = AsDecimal(value);
var rs = fpas.Reverse();
var fas = "";
foreach (var c in rs)
if (c == '0')
continue;
else
fas += c;
return fas.Reverse();
}
public BigRational FractionPart
{
get
{
var rem = BigInteger.Remainder(Numerator, Denominator);
return new BigRational(rem, Denominator);
}
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is BigRational))
return false;
return Equals((BigRational)obj);
}
public override int GetHashCode()
{
return (Numerator / Denominator).GetHashCode();
}
int IComparable.CompareTo(object obj)
{
if (obj == null)
return 1;
if (!(obj is BigRational))
throw new ArgumentException();
return Compare(this, (BigRational)obj);
}
public int CompareTo(BigRational other)
{
return Compare(this, other);
}
public bool Equals(BigRational other)
{
if (Denominator == other.Denominator)
return Numerator == other.Numerator;
return Numerator * other.Denominator == Denominator * other.Numerator;
}
public BigRational(BigInteger numerator)
{
Numerator = numerator;
Denominator = BigInteger.One;
}
public BigRational(string n, string d)
{
Numerator = new BigInteger().BigIntegerBase10(n);
Denominator = new BigInteger().BigIntegerBase10(d);
}
public BigRational(string value)
{
if (!value.ContainsOnly("0123456789+-.eE"))
throw new Exception(
$"Input value must only contain these '0123456789+-.eE', value'{value}");
var v1 = new BigDecimal(value);
var (unscaledValue, scale) = v1.ToByteArrays();
if (v1 == BigDecimal.Zero)
{
this = Zero;
return;
}
Numerator = new BigInteger(unscaledValue);
Denominator = BigInteger.Pow(10, BitConverter.ToInt32(scale, 0));
Simplify();
}
public static bool TryParse(string parse, out BigRational result)
{
result = default;
if (!parse.ContainsOnly("0123456789+-.eE"))
throw new Exception(
$"Input value must only contain these '0123456789+-.eE', value'{parse}");
try
{
result = new BigRational(parse);
}
catch
{
return false;
}
return true;
}
public BigRational(double value) : this((decimal)value)
{
}
public BigRational(BigDecimal value)
{
var bits = value.ToByteArrays();
if (value == BigDecimal.Zero)
{
this = Zero;
return;
}
Numerator = new BigInteger(bits.unscaledValue);
Denominator = BigInteger.Pow(10, BitConverter.ToInt32(bits.scale, 0));
Simplify();
}
public BigRational(decimal value)
{
var bits = decimal.GetBits(value);
if (bits == null || bits.Length != 4 ||
(bits[3] & ~(DecimalSignMask | DecimalScaleMask)) != 0 ||
(bits[3] & DecimalScaleMask) > 28 << 16)
throw new ArgumentException();
if (value == decimal.Zero)
{
this = Zero;
return;
}
var ul = ((ulong)(uint)bits[2] << 32) | (uint)bits[1];
Numerator = (new BigInteger(ul) << 32) | (uint)bits[0];
var isNegative = (bits[3] & DecimalSignMask) != 0;
if (isNegative)
Numerator = BigInteger.Negate(Numerator);
var scale = (bits[3] & DecimalScaleMask) >> 16;
Denominator = BigInteger.Pow(10, scale);
Simplify();
}
public BigRational(BigInteger numerator, BigInteger denominator)
{
if (denominator.Sign == 0)
throw new DivideByZeroException();
if (numerator.Sign == 0)
{
Numerator = BigInteger.Zero;
Denominator = BigInteger.One;
}
else if (denominator.Sign < 0)
{
Numerator = BigInteger.Negate(numerator);
Denominator = BigInteger.Negate(denominator);
}
else
{
Numerator = numerator;
Denominator = denominator;
}
Simplify();
}
public BigRational(BigInteger whole, BigInteger numerator, BigInteger denominator)
{
if (denominator.Sign == 0)
throw new DivideByZeroException();
if (numerator.Sign == 0 && whole.Sign == 0)
{
Numerator = BigInteger.Zero;
Denominator = BigInteger.One;
}
else if (denominator.Sign < 0)
{
Denominator = BigInteger.Negate(denominator);
Numerator = BigInteger.Negate(whole) * Denominator + BigInteger.Negate(numerator);
}
else
{
Denominator = denominator;
Numerator = whole * denominator + numerator;
}
Simplify();
}
public static BigRational Abs(BigRational r)
{
return r.Numerator.Sign < 0
? new BigRational(BigInteger.Abs(r.Numerator), r.Denominator)
: r;
}
public static BigRational Negate(BigRational r)
{
return new BigRational(BigInteger.Negate(r.Numerator), r.Denominator);
}
public static BigRational Invert(BigRational r)
{
return new BigRational(r.Denominator, r.Numerator);
}
public static BigRational Add(BigRational x, BigRational y)
{
return x + y;
}
public static BigRational Subtract(BigRational x, BigRational y)
{
return x - y;
}
public static BigRational Multiply(BigRational x, BigRational y)
{
return x * y;
}
public static BigRational Divide(BigRational dividend, BigRational divisor)
{
return dividend / divisor;
}
public static BigRational DivRem(BigRational dividend,
BigRational divisor,
out BigRational remainder)
{
var ad = dividend.Numerator * divisor.Denominator;
var bc = dividend.Denominator * divisor.Numerator;
var bd = dividend.Denominator * divisor.Denominator;
remainder = new BigRational(ad % bc, bd);
return new BigRational(ad, bc);
}
public static BigInteger LeastCommonDenominator(BigRational x, BigRational y)
{
return x.Denominator * y.Denominator /
BigInteger.GreatestCommonDivisor(x.Denominator, y.Denominator);
}
public static int Compare(BigRational r1, BigRational r2)
{
return BigInteger.Compare(r1.Numerator * r2.Denominator, r2.Numerator * r1.Denominator);
}
public static bool operator ==(BigRational x, BigRational y)
{
return Compare(x, y) == 0;
}
public static bool operator !=(BigRational x, BigRational y)
{
return Compare(x, y) != 0;
}
public static bool operator <(BigRational x, BigRational y)
{
return Compare(x, y) < 0;
}
public static bool operator <=(BigRational x, BigRational y)
{
return Compare(x, y) <= 0;
}
public static bool operator >(BigRational x, BigRational y)
{
return Compare(x, y) > 0;
}
public static bool operator >=(BigRational x, BigRational y)
{
return Compare(x, y) >= 0;
}
public static BigRational operator +(BigRational r)
{
return r;
}
public static BigRational operator -(BigRational r)
{
return new BigRational(-r.Numerator, r.Denominator);
}
public static BigRational operator ++(BigRational r)
{
return r + One;
}
public static BigRational operator --(BigRational r)
{
return r - One;
}
public static BigRational operator +(BigRational r1, BigRational r2)
{
return new BigRational(r1.Numerator * r2.Denominator + r1.Denominator * r2.Numerator,
r1.Denominator * r2.Denominator);
}
public static BigRational operator -(BigRational r1, BigRational r2)
{
return new BigRational(r1.Numerator * r2.Denominator - r1.Denominator * r2.Numerator,
r1.Denominator * r2.Denominator);
}
public static BigRational operator *(BigRational r1, BigRational r2)
{
return new BigRational(r1.Numerator * r2.Numerator, r1.Denominator * r2.Denominator);
}
public static BigRational operator /(BigRational r1, BigRational r2)
{
return new BigRational(r1.Numerator * r2.Denominator, r1.Denominator * r2.Numerator);
}
public static BigRational operator %(BigRational r1, BigRational r2)
{
return new BigRational(r1.Numerator * r2.Denominator % (r1.Denominator * r2.Numerator),
r1.Denominator * r2.Denominator);
}
public static explicit operator sbyte(BigRational value)
{
return (sbyte)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator ushort(BigRational value)
{
return (ushort)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator uint(BigRational value)
{
return (uint)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator ulong(BigRational value)
{
return (ulong)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator byte(BigRational value)
{
return (byte)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator short(BigRational value)
{
return (short)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator int(BigRational value)
{
return (int)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator long(BigRational value)
{
return (long)BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator BigInteger(BigRational value)
{
return BigInteger.Divide(value.Numerator, value.Denominator);
}
public static explicit operator float(BigRational value)
{
return (float)(double)value;
}
public static explicit operator double(BigRational value)
{
if (SafeCastToDouble(value.Numerator) && SafeCastToDouble(value.Denominator))
return (double)value.Numerator / (double)value.Denominator;
var denormalized = value.Numerator * DoublePrecision / value.Denominator;
if (denormalized.IsZero)
return value.Sign < 0
? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000))
: 0d;
double result = 0;
var isDouble = false;
var scale = DoubleMaxScale;
while (scale > 0)
{
if (!isDouble)
if (SafeCastToDouble(denormalized))
{
result = (double)denormalized;
isDouble = true;
}
else
{
denormalized = denormalized / 10;
}
result = result / 10;
scale--;
}
if (!isDouble)
return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity;
return result;
}
public static explicit operator BigDecimal(BigRational value)
{
var denormalized = value.Numerator * DecimalPrecision / value.Denominator;
return new BigDecimal(denormalized, (int)DecimalMaxScale);
}
public static explicit operator decimal(BigRational value)
{
if (SafeCastToDecimal(value.Numerator) && SafeCastToDecimal(value.Denominator))
return (decimal)value.Numerator / (decimal)value.Denominator;
var denormalized = value.Numerator * DecimalPrecision / value.Denominator;
if (denormalized.IsZero)
return decimal.Zero;
for (var scale = (int)DecimalMaxScale; scale >= 0; scale--)
if (!SafeCastToDecimal(denormalized))
{
denormalized /= 10;
}
else
{
var dec = new DecimalUInt32();
dec.dec = (decimal)denormalized;
dec.flags = (dec.flags & ~DecimalScaleMask) | (scale << 16);
return dec.dec;
}
throw new OverflowException();
}
public static implicit operator BigRational(sbyte value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(ushort value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(uint value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(ulong value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(byte value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(short value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(int value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(long value)
{
return new BigRational((BigInteger)value);
}
public static implicit operator BigRational(BigInteger value)
{
return new BigRational(value);
}
public static implicit operator BigRational(string value)
{
return new BigRational(value);
}
public static implicit operator BigRational(float value)
{
return new BigRational(value);
}
public static implicit operator BigRational(double value)
{
return new BigRational(value);
}
public static implicit operator BigRational(decimal value)
{
return new BigRational(value);
}
public static implicit operator BigRational(BigDecimal value)
{
return new BigRational(value);
}
private void Simplify()
{
if (Numerator == BigInteger.Zero)
Denominator = BigInteger.One;
var gcd = BigInteger.GreatestCommonDivisor(Numerator, Denominator);
if (gcd > BigInteger.One)
{
Numerator = Numerator / gcd;
Denominator = Denominator / gcd;
}
}
private static bool SafeCastToDouble(BigInteger value)
{
return DoubleMinValue <= value && value <= DoubleMaxValue;
}
private static bool SafeCastToDecimal(BigInteger value)
{
return DecimalMinValue <= value && value <= DecimalMaxValue;
}
private static void SplitDoubleIntoParts(double dbl,
out int sign,
out int exp,
out ulong man,
out bool isFinite)
{
DoubleUlong du;
du.uu = 0;
du.dbl = dbl;
sign = 1 - ((int)(du.uu >> 62) & 2);
man = du.uu & 0x000FFFFFFFFFFFFF;
exp = (int)(du.uu >> 52) & 0x7FF;
if (exp == 0)
{
isFinite = true;
if (man != 0)
exp = -1074;
}
else if (exp == 0x7FF)
{
isFinite = false;
exp = int.MaxValue;
}
else
{
isFinite = true;
man |= 0x0010000000000000;
exp -= 1075;
}
}
public static double GetDoubleFromParts(int sign, int exp, ulong man)
{
DoubleUlong du;
du.dbl = 0;
if (man == 0)
{
du.uu = 0;
}
else
{
var cbitShift = CbitHighZero(man) - 11;
if (cbitShift < 0)
man >>= -cbitShift;
else
man <<= cbitShift;
exp += 1075;
if (exp >= 0x7FF)
{
du.uu = 0x7FF0000000000000;
}
else if (exp <= 0)
{
exp--;
if (exp < -52)
du.uu = 0;
else
du.uu = man >> -exp;
}
else
{
du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong)exp << 52);
}
}
if (sign < 0)
du.uu |= 0x8000000000000000;
return du.dbl;
}
private static int CbitHighZero(ulong uu)
{
if ((uu & 0xFFFFFFFF00000000) == 0)
return 32 + CbitHighZero((uint)uu);
return CbitHighZero((uint)(uu >> 32));
}
private static int CbitHighZero(uint u)
{
if (u == 0)
return 32;
var cbit = 0;
if ((u & 0xFFFF0000) == 0)
{
cbit += 16;
u <<= 16;
}
if ((u & 0xFF000000) == 0)
{
cbit += 8;
u <<= 8;
}
if ((u & 0xF0000000) == 0)
{
cbit += 4;
u <<= 4;
}
if ((u & 0xC0000000) == 0)
{
cbit += 2;
u <<= 2;
}
if ((u & 0x80000000) == 0)
cbit += 1;
return cbit;
}
private static (BigRational High, BigRational Low) SqrtLimits(BigInteger number)
{
if (number == BigInteger.Zero) return (0, 0);
var high = number >> 1;
var low = BigInteger.Zero;
while (high > low + 1)
{
var n = (high + low) >> 1;
var p = n * n;
if (number < p)
high = n;
else if (number > p)
low = n;
else
break;
}
return (high, low);
}
public static BigRational Sqrt(BigRational value)
{
if (value == 0) return 0;
var hl = SqrtLimits(value.WholePart);
BigRational n = 0, p = 0;
if (hl.High == 0 && hl.Low == 0)
return 0;
var high = hl.High;
var low = hl.Low;
var d = DecimalPrecision;
var pp = 1 / (BigRational)d;
while (high > low + pp)
{
n = (high + low) / 2;
p = n * n;
if (value < p)
high = n;
else if (value > p)
low = n;
else
break;
}
var r = value == p ? n : low;
return r;
}
public BigRational Sqrt()
{
return Sqrt(this);
}
public static BigRational ArcTangent(BigRational v, int n)
{
var retVal = v;
for (var i = 1; i < n; i++)
{
var powRat = Pow(v, 2 * i + 1);
retVal += new BigRational(powRat.Numerator * (BigInteger)Math.Pow(-1d, i),
(2 * i + 1) * powRat.Denominator);
}
return retVal;
}
public static BigRational Reciprocal(BigRational v)
{
return new BigRational(v.Denominator, v.Numerator);
}
public static BigRational Round(BigRational number, int decimalPlaces)
{
BigRational power = BigInteger.Pow(10, decimalPlaces);
number *= power;
return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
}
public void Round(int decimalPlaces)
{
var number = this;
BigRational power = BigInteger.Pow(10, decimalPlaces);
number *= power;
var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
Numerator = n.Numerator;
Denominator = n.Denominator;
}
public static BigRational Pow(BigRational v, int e)
{
if (e < 1) throw new ArgumentException("Powers must be greater than or equal to one.");
var retVal = new BigRational(v.Numerator, v.Denominator);
for (var i = 1; i < e; i++)
{
retVal.Numerator *= v.Numerator;
retVal.Denominator *= v.Denominator;
}
return retVal;
}
public static BigRational Min(BigRational r, BigRational l)
{
return l < r ? l : r;
}
public static BigRational Max(BigRational r, BigRational l)
{
return l > r ? l : r;
}
/// <summary>
/// Set Pi before call
/// </summary>
public static BigRational ToRadians(BigRational degrees)
{
return degrees * Pi / 180;
}
/// <summary>
/// Set Pi before call
/// </summary>
public static BigRational ToDegrees(BigRational rads)
{
return rads * 180 / Pi;
}
private static BigRational Factorial(BigRational x)
{
BigRational r = 1;
BigRational c = 1;
while (c <= x)
{
r *= c;
c++;
}
return r;
}
public static BigRational Exp(BigRational x)
{
BigRational r = 0;
BigRational r1 = 0;
var k = 0;
while (true)
{
r += Pow(x, k) / Factorial(k);
if (r == r1)
break;
r1 = r;
k++;
}
return r;
}
public static BigRational Sine(BigRational ar, int n)
{
if (Factorials == null)
{
Factorials = new BigRational[MaxFactorials];
for (var i = 0; i < MaxFactorials; i++)
Factorials[i] = new BigRational();
for (var i = 1; i < MaxFactorials + 1; i++)
Factorials[i - 1] = Factorial(i);
}
var sin = ar;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2 + 1);
trm /= Factorials[i * 2];
if ((i & 1) == 1)
sin -= trm;
else
sin += trm;
}
return sin;
}
public static BigRational Atan(BigRational ar, int n)
{
var atan = ar;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2 + 1);
trm /= i * 2;
if ((i & 1) == 1)
atan -= trm;
else
atan += trm;
}
return atan;
}
public static BigRational Cosine(BigRational ar, int n)
{
if (Factorials == null)
{
Factorials = new BigRational[MaxFactorials];
for (var i = 0; i < MaxFactorials; i++)
Factorials[i] = new BigRational();
for (var i = 1; i < MaxFactorials + 1; i++)
Factorials[i - 1] = Factorial(i);
}
BigRational cos = 1.0;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2);
trm /= Factorials[i * 2 - 1];
if ((i & 1) == 1)
cos -= trm;
else
cos += trm;
}
return cos;
}
public static BigRational Tangent(BigRational ar, int n)
{
return Sine(ar, n) / Cosine(ar, n);
}
public static BigRational CoTangent(BigRational ar, int n)
{
return Cosine(ar, n) / Sine(ar, n);
}
public static BigRational Secant(BigRational ar, int n)
{
return 1.0 / Cosine(ar, n);
}
public static BigRational CoSecant(BigRational ar, int n)
{
return 1.0 / Sine(ar, n);
}
private static BigRational GetE(int n)
{
BigRational e = 1.0;
var c = n;
while (c > 0)
{
BigRational f = 0;
if (c == 1)
{
f = 1;
}
else
{
var i = c - 1;
f = c;
while (i > 0)
{
f *= i;
i--;
}
}
c--;
e += 1.0 / f;
}
return e;
}
public static BigRational NthRoot(BigRational value, int nth)
{
BigRational lx;
var a = value;
var n = nth;
BigRational s = 1.0;
do
{
var t = s;
lx = a / Pow(s, n - 1);
var r = (n - 1) * s;
s = (lx + r) / n;
} while (lx != s);
return s;
}
public static BigRational LogN(BigRational value)
{
BigRational a;
var p = value;
BigRational n = 0.0;
while (p >= E)
{
p /= E;
n++;
}
n += p / E;
p = value;
do
{
a = n;
var lx = p / Exp(n - 1.0);
var r = (n - 1.0) * E;
n = (lx + r) / E;
} while (n != a);
return n;
}
public static BigRational Log(BigRational n, int b)
{
return LogN(n) / LogN(b);
}
private static int ConversionIterations(BigRational v)
{
return (int)((DecimalMaxScale + 1) / (2 * Math.Log10((double)Reciprocal(v))));
}
public static BigRational GetPI()
{
var oneFifth = new BigRational(1, 5);
var oneTwoThirtyNine = new BigRational(1, 239);
var arcTanOneFifth = ArcTangent(oneFifth, ConversionIterations(oneFifth));
var arcTanOneTwoThirtyNine =
ArcTangent(oneTwoThirtyNine, ConversionIterations(oneTwoThirtyNine));
return arcTanOneFifth * 16 - arcTanOneTwoThirtyNine * 4;
}
public override string ToString()
{
var ret = new StringBuilder();
ret.Append(Numerator.ToString("R", CultureInfo.InvariantCulture));
ret.Append(Solidus);
ret.Append(Denominator.ToString("R", CultureInfo.InvariantCulture));
return ret.ToString();
}
}
using System; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Text; [DebuggerDisplay("{" + nameof(DDisplay) + "}")] [Serializable] public struct BigRational : IComparable, IComparable<BigRational>, IEquatable<BigRational> { [StructLayout(LayoutKind.Explicit)] internal struct DoubleUlong { [FieldOffset(0)] public double dbl; [FieldOffset(0)] public ulong uu; } /// <summary> /// Change here if more then 2048 bits are specified /// </summary> private const float DecimalMaxScale = 2048f / 64f * 20f; private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, (int)DecimalMaxScale); private const int DoubleMaxScale = 308; public static BigRational Pi = new( "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798162478513934506898440362801792706010179987216806726188740140466033567581311679376075335101609659171030644576233653027450257182803484658351860927270133809030914436823660262931162576284703194589395221866245992710817555393680237554917047871708932985106840785074833639247080859264327721882027979677397953754604196915619381410505600288856897761875941052867609089114345150157869223684881643245943313338421018485091403977277400743970527492816321894223953257584787737337170568053925027217102844351208765657302025589127695185039186644597240030541171074757870137431100579097277905612641495178817964173941740654985445918326928220945355416048444887050935562696866696019631573868714587428709669938320262709342763"); public static BigRational E = GetE(MaxFactorials); private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, DoubleMaxScale); private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue; private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue; private static BigRational[] Factorials; static BigRational() { } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => AsDecimal(this); [StructLayout(LayoutKind.Explicit)] internal struct DecimalUInt32 { [FieldOffset(0)] public decimal dec; [FieldOffset(0)] public int flags; } private const int DecimalScaleMask = 0x00FF0000; private const int DecimalSignMask = unchecked((int)0x80000000); private const int MaxFactorials = 100; private static readonly BigInteger DecimalMaxValue = (BigInteger)decimal.MaxValue; private static readonly BigInteger DecimalMinValue = (BigInteger)decimal.MinValue; private const string Solidus = @"/"; public static BigRational Zero { get; } = new(BigInteger.Zero); public static BigRational One { get; } = new(BigInteger.One); public static BigRational MinusOne { get; } = new(BigInteger.MinusOne); public int Sign => Numerator.Sign; public BigInteger Numerator { get; private set; } public BigInteger Denominator { get; private set; } public BigInteger WholePart => BigInteger.Divide(Numerator, Denominator); public bool IsFractionalPart { get { var fp = FractionPart; return fp.Numerator != 0 || fp.Denominator != 1; } } public BigInteger GetUnscaledAsDecimal => Numerator * DecimalPrecision / Denominator; public BigInteger Remainder => Numerator % Denominator; public int DecimalPlaces { get { var a = GetUnscaledAsDecimal; var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new BigInteger(10); while (a > 0) try { a /= biRadix; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } } public static string AsDecimal(BigRational value) { var asd = new BigDecimal(value); return asd.ToString(); } public static string CleanAsDecimal(BigRational value) { var fpas = AsDecimal(value); var rs = fpas.Reverse(); var fas = ""; foreach (var c in rs) if (c == '0') continue; else fas += c; return fas.Reverse(); } public BigRational FractionPart { get { var rem = BigInteger.Remainder(Numerator, Denominator); return new BigRational(rem, Denominator); } } public override bool Equals(object obj) { if (obj == null) return false; if (!(obj is BigRational)) return false; return Equals((BigRational)obj); } public override int GetHashCode() { return (Numerator / Denominator).GetHashCode(); } int IComparable.CompareTo(object obj) { if (obj == null) return 1; if (!(obj is BigRational)) throw new ArgumentException(); return Compare(this, (BigRational)obj); } public int CompareTo(BigRational other) { return Compare(this, other); } public bool Equals(BigRational other) { if (Denominator == other.Denominator) return Numerator == other.Numerator; return Numerator * other.Denominator == Denominator * other.Numerator; } public BigRational(BigInteger numerator) { Numerator = numerator; Denominator = BigInteger.One; } public BigRational(string n, string d) { Numerator = new BigInteger().BigIntegerBase10(n); Denominator = new BigInteger().BigIntegerBase10(d); } public BigRational(string value) { if (!value.ContainsOnly("0123456789+-.eE")) throw new Exception( $"Input value must only contain these '0123456789+-.eE', value'{value}"); var v1 = new BigDecimal(value); var (unscaledValue, scale) = v1.ToByteArrays(); if (v1 == BigDecimal.Zero) { this = Zero; return; } Numerator = new BigInteger(unscaledValue); Denominator = BigInteger.Pow(10, BitConverter.ToInt32(scale, 0)); Simplify(); } public static bool TryParse(string parse, out BigRational result) { result = default; if (!parse.ContainsOnly("0123456789+-.eE")) throw new Exception( $"Input value must only contain these '0123456789+-.eE', value'{parse}"); try { result = new BigRational(parse); } catch { return false; } return true; } public BigRational(double value) : this((decimal)value) { } public BigRational(BigDecimal value) { var bits = value.ToByteArrays(); if (value == BigDecimal.Zero) { this = Zero; return; } Numerator = new BigInteger(bits.unscaledValue); Denominator = BigInteger.Pow(10, BitConverter.ToInt32(bits.scale, 0)); Simplify(); } public BigRational(decimal value) { var bits = decimal.GetBits(value); if (bits == null || bits.Length != 4 || (bits[3] & ~(DecimalSignMask | DecimalScaleMask)) != 0 || (bits[3] & DecimalScaleMask) > 28 << 16) throw new ArgumentException(); if (value == decimal.Zero) { this = Zero; return; } var ul = ((ulong)(uint)bits[2] << 32) | (uint)bits[1]; Numerator = (new BigInteger(ul) << 32) | (uint)bits[0]; var isNegative = (bits[3] & DecimalSignMask) != 0; if (isNegative) Numerator = BigInteger.Negate(Numerator); var scale = (bits[3] & DecimalScaleMask) >> 16; Denominator = BigInteger.Pow(10, scale); Simplify(); } public BigRational(BigInteger numerator, BigInteger denominator) { if (denominator.Sign == 0) throw new DivideByZeroException(); if (numerator.Sign == 0) { Numerator = BigInteger.Zero; Denominator = BigInteger.One; } else if (denominator.Sign < 0) { Numerator = BigInteger.Negate(numerator); Denominator = BigInteger.Negate(denominator); } else { Numerator = numerator; Denominator = denominator; } Simplify(); } public BigRational(BigInteger whole, BigInteger numerator, BigInteger denominator) { if (denominator.Sign == 0) throw new DivideByZeroException(); if (numerator.Sign == 0 && whole.Sign == 0) { Numerator = BigInteger.Zero; Denominator = BigInteger.One; } else if (denominator.Sign < 0) { Denominator = BigInteger.Negate(denominator); Numerator = BigInteger.Negate(whole) * Denominator + BigInteger.Negate(numerator); } else { Denominator = denominator; Numerator = whole * denominator + numerator; } Simplify(); } public static BigRational Abs(BigRational r) { return r.Numerator.Sign < 0 ? new BigRational(BigInteger.Abs(r.Numerator), r.Denominator) : r; } public static BigRational Negate(BigRational r) { return new BigRational(BigInteger.Negate(r.Numerator), r.Denominator); } public static BigRational Invert(BigRational r) { return new BigRational(r.Denominator, r.Numerator); } public static BigRational Add(BigRational x, BigRational y) { return x + y; } public static BigRational Subtract(BigRational x, BigRational y) { return x - y; } public static BigRational Multiply(BigRational x, BigRational y) { return x * y; } public static BigRational Divide(BigRational dividend, BigRational divisor) { return dividend / divisor; } public static BigRational DivRem(BigRational dividend, BigRational divisor, out BigRational remainder) { var ad = dividend.Numerator * divisor.Denominator; var bc = dividend.Denominator * divisor.Numerator; var bd = dividend.Denominator * divisor.Denominator; remainder = new BigRational(ad % bc, bd); return new BigRational(ad, bc); } public static BigInteger LeastCommonDenominator(BigRational x, BigRational y) { return x.Denominator * y.Denominator / BigInteger.GreatestCommonDivisor(x.Denominator, y.Denominator); } public static int Compare(BigRational r1, BigRational r2) { return BigInteger.Compare(r1.Numerator * r2.Denominator, r2.Numerator * r1.Denominator); } public static bool operator ==(BigRational x, BigRational y) { return Compare(x, y) == 0; } public static bool operator !=(BigRational x, BigRational y) { return Compare(x, y) != 0; } public static bool operator <(BigRational x, BigRational y) { return Compare(x, y) < 0; } public static bool operator <=(BigRational x, BigRational y) { return Compare(x, y) <= 0; } public static bool operator >(BigRational x, BigRational y) { return Compare(x, y) > 0; } public static bool operator >=(BigRational x, BigRational y) { return Compare(x, y) >= 0; } public static BigRational operator +(BigRational r) { return r; } public static BigRational operator -(BigRational r) { return new BigRational(-r.Numerator, r.Denominator); } public static BigRational operator ++(BigRational r) { return r + One; } public static BigRational operator --(BigRational r) { return r - One; } public static BigRational operator +(BigRational r1, BigRational r2) { return new BigRational(r1.Numerator * r2.Denominator + r1.Denominator * r2.Numerator, r1.Denominator * r2.Denominator); } public static BigRational operator -(BigRational r1, BigRational r2) { return new BigRational(r1.Numerator * r2.Denominator - r1.Denominator * r2.Numerator, r1.Denominator * r2.Denominator); } public static BigRational operator *(BigRational r1, BigRational r2) { return new BigRational(r1.Numerator * r2.Numerator, r1.Denominator * r2.Denominator); } public static BigRational operator /(BigRational r1, BigRational r2) { return new BigRational(r1.Numerator * r2.Denominator, r1.Denominator * r2.Numerator); } public static BigRational operator %(BigRational r1, BigRational r2) { return new BigRational(r1.Numerator * r2.Denominator % (r1.Denominator * r2.Numerator), r1.Denominator * r2.Denominator); } public static explicit operator sbyte(BigRational value) { return (sbyte)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator ushort(BigRational value) { return (ushort)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator uint(BigRational value) { return (uint)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator ulong(BigRational value) { return (ulong)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator byte(BigRational value) { return (byte)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator short(BigRational value) { return (short)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator int(BigRational value) { return (int)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator long(BigRational value) { return (long)BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator BigInteger(BigRational value) { return BigInteger.Divide(value.Numerator, value.Denominator); } public static explicit operator float(BigRational value) { return (float)(double)value; } public static explicit operator double(BigRational value) { if (SafeCastToDouble(value.Numerator) && SafeCastToDouble(value.Denominator)) return (double)value.Numerator / (double)value.Denominator; var denormalized = value.Numerator * DoublePrecision / value.Denominator; if (denormalized.IsZero) return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d; double result = 0; var isDouble = false; var scale = DoubleMaxScale; while (scale > 0) { if (!isDouble) if (SafeCastToDouble(denormalized)) { result = (double)denormalized; isDouble = true; } else { denormalized = denormalized / 10; } result = result / 10; scale--; } if (!isDouble) return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity; return result; } public static explicit operator BigDecimal(BigRational value) { var denormalized = value.Numerator * DecimalPrecision / value.Denominator; return new BigDecimal(denormalized, (int)DecimalMaxScale); } public static explicit operator decimal(BigRational value) { if (SafeCastToDecimal(value.Numerator) && SafeCastToDecimal(value.Denominator)) return (decimal)value.Numerator / (decimal)value.Denominator; var denormalized = value.Numerator * DecimalPrecision / value.Denominator; if (denormalized.IsZero) return decimal.Zero; for (var scale = (int)DecimalMaxScale; scale >= 0; scale--) if (!SafeCastToDecimal(denormalized)) { denormalized /= 10; } else { var dec = new DecimalUInt32(); dec.dec = (decimal)denormalized; dec.flags = (dec.flags & ~DecimalScaleMask) | (scale << 16); return dec.dec; } throw new OverflowException(); } public static implicit operator BigRational(sbyte value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(ushort value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(uint value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(ulong value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(byte value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(short value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(int value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(long value) { return new BigRational((BigInteger)value); } public static implicit operator BigRational(BigInteger value) { return new BigRational(value); } public static implicit operator BigRational(string value) { return new BigRational(value); } public static implicit operator BigRational(float value) { return new BigRational(value); } public static implicit operator BigRational(double value) { return new BigRational(value); } public static implicit operator BigRational(decimal value) { return new BigRational(value); } public static implicit operator BigRational(BigDecimal value) { return new BigRational(value); } private void Simplify() { if (Numerator == BigInteger.Zero) Denominator = BigInteger.One; var gcd = BigInteger.GreatestCommonDivisor(Numerator, Denominator); if (gcd > BigInteger.One) { Numerator = Numerator / gcd; Denominator = Denominator / gcd; } } private static bool SafeCastToDouble(BigInteger value) { return DoubleMinValue <= value && value <= DoubleMaxValue; } private static bool SafeCastToDecimal(BigInteger value) { return DecimalMinValue <= value && value <= DecimalMaxValue; } private static void SplitDoubleIntoParts(double dbl, out int sign, out int exp, out ulong man, out bool isFinite) { DoubleUlong du; du.uu = 0; du.dbl = dbl; sign = 1 - ((int)(du.uu >> 62) & 2); man = du.uu & 0x000FFFFFFFFFFFFF; exp = (int)(du.uu >> 52) & 0x7FF; if (exp == 0) { isFinite = true; if (man != 0) exp = -1074; } else if (exp == 0x7FF) { isFinite = false; exp = int.MaxValue; } else { isFinite = true; man |= 0x0010000000000000; exp -= 1075; } } public static double GetDoubleFromParts(int sign, int exp, ulong man) { DoubleUlong du; du.dbl = 0; if (man == 0) { du.uu = 0; } else { var cbitShift = CbitHighZero(man) - 11; if (cbitShift < 0) man >>= -cbitShift; else man <<= cbitShift; exp += 1075; if (exp >= 0x7FF) { du.uu = 0x7FF0000000000000; } else if (exp <= 0) { exp--; if (exp < -52) du.uu = 0; else du.uu = man >> -exp; } else { du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong)exp << 52); } } if (sign < 0) du.uu |= 0x8000000000000000; return du.dbl; } private static int CbitHighZero(ulong uu) { if ((uu & 0xFFFFFFFF00000000) == 0) return 32 + CbitHighZero((uint)uu); return CbitHighZero((uint)(uu >> 32)); } private static int CbitHighZero(uint u) { if (u == 0) return 32; var cbit = 0; if ((u & 0xFFFF0000) == 0) { cbit += 16; u <<= 16; } if ((u & 0xFF000000) == 0) { cbit += 8; u <<= 8; } if ((u & 0xF0000000) == 0) { cbit += 4; u <<= 4; } if ((u & 0xC0000000) == 0) { cbit += 2; u <<= 2; } if ((u & 0x80000000) == 0) cbit += 1; return cbit; } private static (BigRational High, BigRational Low) SqrtLimits(BigInteger number) { if (number == BigInteger.Zero) return (0, 0); var high = number >> 1; var low = BigInteger.Zero; while (high > low + 1) { var n = (high + low) >> 1; var p = n * n; if (number < p) high = n; else if (number > p) low = n; else break; } return (high, low); } public static BigRational Sqrt(BigRational value) { if (value == 0) return 0; var hl = SqrtLimits(value.WholePart); BigRational n = 0, p = 0; if (hl.High == 0 && hl.Low == 0) return 0; var high = hl.High; var low = hl.Low; var d = DecimalPrecision; var pp = 1 / (BigRational)d; while (high > low + pp) { n = (high + low) / 2; p = n * n; if (value < p) high = n; else if (value > p) low = n; else break; } var r = value == p ? n : low; return r; } public BigRational Sqrt() { return Sqrt(this); } public static BigRational ArcTangent(BigRational v, int n) { var retVal = v; for (var i = 1; i < n; i++) { var powRat = Pow(v, 2 * i + 1); retVal += new BigRational(powRat.Numerator * (BigInteger)Math.Pow(-1d, i), (2 * i + 1) * powRat.Denominator); } return retVal; } public static BigRational Reciprocal(BigRational v) { return new BigRational(v.Denominator, v.Numerator); } public static BigRational Round(BigRational number, int decimalPlaces) { BigRational power = BigInteger.Pow(10, decimalPlaces); number *= power; return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; } public void Round(int decimalPlaces) { var number = this; BigRational power = BigInteger.Pow(10, decimalPlaces); number *= power; var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; Numerator = n.Numerator; Denominator = n.Denominator; } public static BigRational Pow(BigRational v, int e) { if (e < 1) throw new ArgumentException("Powers must be greater than or equal to one."); var retVal = new BigRational(v.Numerator, v.Denominator); for (var i = 1; i < e; i++) { retVal.Numerator *= v.Numerator; retVal.Denominator *= v.Denominator; } return retVal; } public static BigRational Min(BigRational r, BigRational l) { return l < r ? l : r; } public static BigRational Max(BigRational r, BigRational l) { return l > r ? l : r; } /// <summary> /// Set Pi before call /// </summary> public static BigRational ToRadians(BigRational degrees) { return degrees * Pi / 180; } /// <summary> /// Set Pi before call /// </summary> public static BigRational ToDegrees(BigRational rads) { return rads * 180 / Pi; } private static BigRational Factorial(BigRational x) { BigRational r = 1; BigRational c = 1; while (c <= x) { r *= c; c++; } return r; } public static BigRational Exp(BigRational x) { BigRational r = 0; BigRational r1 = 0; var k = 0; while (true) { r += Pow(x, k) / Factorial(k); if (r == r1) break; r1 = r; k++; } return r; } public static BigRational Sine(BigRational ar, int n) { if (Factorials == null) { Factorials = new BigRational[MaxFactorials]; for (var i = 0; i < MaxFactorials; i++) Factorials[i] = new BigRational(); for (var i = 1; i < MaxFactorials + 1; i++) Factorials[i - 1] = Factorial(i); } var sin = ar; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2 + 1); trm /= Factorials[i * 2]; if ((i & 1) == 1) sin -= trm; else sin += trm; } return sin; } public static BigRational Atan(BigRational ar, int n) { var atan = ar; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2 + 1); trm /= i * 2; if ((i & 1) == 1) atan -= trm; else atan += trm; } return atan; } public static BigRational Cosine(BigRational ar, int n) { if (Factorials == null) { Factorials = new BigRational[MaxFactorials]; for (var i = 0; i < MaxFactorials; i++) Factorials[i] = new BigRational(); for (var i = 1; i < MaxFactorials + 1; i++) Factorials[i - 1] = Factorial(i); } BigRational cos = 1.0; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2); trm /= Factorials[i * 2 - 1]; if ((i & 1) == 1) cos -= trm; else cos += trm; } return cos; } public static BigRational Tangent(BigRational ar, int n) { return Sine(ar, n) / Cosine(ar, n); } public static BigRational CoTangent(BigRational ar, int n) { return Cosine(ar, n) / Sine(ar, n); } public static BigRational Secant(BigRational ar, int n) { return 1.0 / Cosine(ar, n); } public static BigRational CoSecant(BigRational ar, int n) { return 1.0 / Sine(ar, n); } private static BigRational GetE(int n) { BigRational e = 1.0; var c = n; while (c > 0) { BigRational f = 0; if (c == 1) { f = 1; } else { var i = c - 1; f = c; while (i > 0) { f *= i; i--; } } c--; e += 1.0 / f; } return e; } public static BigRational NthRoot(BigRational value, int nth) { BigRational lx; var a = value; var n = nth; BigRational s = 1.0; do { var t = s; lx = a / Pow(s, n - 1); var r = (n - 1) * s; s = (lx + r) / n; } while (lx != s); return s; } public static BigRational LogN(BigRational value) { BigRational a; var p = value; BigRational n = 0.0; while (p >= E) { p /= E; n++; } n += p / E; p = value; do { a = n; var lx = p / Exp(n - 1.0); var r = (n - 1.0) * E; n = (lx + r) / E; } while (n != a); return n; } public static BigRational Log(BigRational n, int b) { return LogN(n) / LogN(b); } private static int ConversionIterations(BigRational v) { return (int)((DecimalMaxScale + 1) / (2 * Math.Log10((double)Reciprocal(v)))); } public static BigRational GetPI() { var oneFifth = new BigRational(1, 5); var oneTwoThirtyNine = new BigRational(1, 239); var arcTanOneFifth = ArcTangent(oneFifth, ConversionIterations(oneFifth)); var arcTanOneTwoThirtyNine = ArcTangent(oneTwoThirtyNine, ConversionIterations(oneTwoThirtyNine)); return arcTanOneFifth * 16 - arcTanOneTwoThirtyNine * 4; } public override string ToString() { var ret = new StringBuilder(); ret.Append(Numerator.ToString("R", CultureInfo.InvariantCulture)); ret.Append(Solidus); ret.Append(Denominator.ToString("R", CultureInfo.InvariantCulture)); return ret.ToString(); } }
using System;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[DebuggerDisplay("{" + nameof(DDisplay) + "}")]
[Serializable]
public struct BigRational : IComparable, IComparable<BigRational>, IEquatable<BigRational>
{
    [StructLayout(LayoutKind.Explicit)]
    internal struct DoubleUlong
    {
        [FieldOffset(0)] public double dbl;
        [FieldOffset(0)] public ulong  uu;
    }
    /// <summary>
    ///     Change here if more then 2048 bits are specified
    /// </summary>
    private const float DecimalMaxScale = 2048f / 64f * 20f;
    private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, (int)DecimalMaxScale);
    private const           int        DoubleMaxScale   = 308;
    public static BigRational Pi = new(
        "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798162478513934506898440362801792706010179987216806726188740140466033567581311679376075335101609659171030644576233653027450257182803484658351860927270133809030914436823660262931162576284703194589395221866245992710817555393680237554917047871708932985106840785074833639247080859264327721882027979677397953754604196915619381410505600288856897761875941052867609089114345150157869223684881643245943313338421018485091403977277400743970527492816321894223953257584787737337170568053925027217102844351208765657302025589127695185039186644597240030541171074757870137431100579097277905612641495178817964173941740654985445918326928220945355416048444887050935562696866696019631573868714587428709669938320262709342763");
    public static           BigRational   E               = GetE(MaxFactorials);
    private static readonly BigInteger    DoublePrecision = BigInteger.Pow(10, DoubleMaxScale);
    private static readonly BigInteger    DoubleMaxValue  = (BigInteger)double.MaxValue;
    private static readonly BigInteger    DoubleMinValue  = (BigInteger)double.MinValue;
    private static          BigRational[] Factorials;
    static BigRational()
    {
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => AsDecimal(this);
    [StructLayout(LayoutKind.Explicit)]
    internal struct DecimalUInt32
    {
        [FieldOffset(0)] public decimal dec;
        [FieldOffset(0)] public int     flags;
    }
    private const           int        DecimalScaleMask = 0x00FF0000;
    private const           int        DecimalSignMask  = unchecked((int)0x80000000);
    private const           int        MaxFactorials    = 100;
    private static readonly BigInteger DecimalMaxValue  = (BigInteger)decimal.MaxValue;
    private static readonly BigInteger DecimalMinValue  = (BigInteger)decimal.MinValue;
    private const           string     Solidus          = @"/";
    public static BigRational Zero
    {
        get;
    } = new(BigInteger.Zero);
    public static BigRational One
    {
        get;
    } = new(BigInteger.One);
    public static BigRational MinusOne
    {
        get;
    } = new(BigInteger.MinusOne);
    public int Sign => Numerator.Sign;
    public BigInteger Numerator
    {
        get;
        private set;
    }
    public BigInteger Denominator
    {
        get;
        private set;
    }
    public BigInteger WholePart => BigInteger.Divide(Numerator, Denominator);
    public bool IsFractionalPart
    {
        get
        {
            var fp = FractionPart;
            return fp.Numerator != 0 || fp.Denominator != 1;
        }
    }
    public BigInteger GetUnscaledAsDecimal => Numerator * DecimalPrecision / Denominator;
    public BigInteger Remainder            => Numerator                    % Denominator;
    public int DecimalPlaces
    {
        get
        {
            var a       = GetUnscaledAsDecimal;
            var dPlaces = 0;
            if (a.Sign == 0)
                return 1;
            if (a.Sign < 0)
                try
                {
                    a = -a;
                }
                catch (Exception ex)
                {
                    return 0;
                }
            var biRadix = new BigInteger(10);
            while (a > 0)
                try
                {
                    a /= biRadix;
                    dPlaces++;
                }
                catch (Exception ex)
                {
                    break;
                }
            return dPlaces;
        }
    }
    public static string AsDecimal(BigRational value)
    {
        var asd = new BigDecimal(value);
        return asd.ToString();
    }
    public static string CleanAsDecimal(BigRational value)
    {
        var fpas = AsDecimal(value);
        var rs   = fpas.Reverse();
        var fas  = "";
        foreach (var c in rs)
            if (c == '0')
                continue;
            else
                fas += c;
        return fas.Reverse();
    }
    public BigRational FractionPart
    {
        get
        {
            var rem = BigInteger.Remainder(Numerator, Denominator);
            return new BigRational(rem, Denominator);
        }
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (!(obj is BigRational))
            return false;
        return Equals((BigRational)obj);
    }
    public override int GetHashCode()
    {
        return (Numerator / Denominator).GetHashCode();
    }
    int IComparable.CompareTo(object obj)
    {
        if (obj == null)
            return 1;
        if (!(obj is BigRational))
            throw new ArgumentException();
        return Compare(this, (BigRational)obj);
    }
    public int CompareTo(BigRational other)
    {
        return Compare(this, other);
    }
    public bool Equals(BigRational other)
    {
        if (Denominator == other.Denominator)
            return Numerator == other.Numerator;
        return Numerator * other.Denominator == Denominator * other.Numerator;
    }
    public BigRational(BigInteger numerator)
    {
        Numerator   = numerator;
        Denominator = BigInteger.One;
    }
    public BigRational(string n, string d)
    {
        Numerator   = new BigInteger().BigIntegerBase10(n);
        Denominator = new BigInteger().BigIntegerBase10(d);
    }
    public BigRational(string value)
    {
        if (!value.ContainsOnly("0123456789+-.eE"))
            throw new Exception(
                $"Input value must only contain these '0123456789+-.eE', value'{value}");
        var v1 = new BigDecimal(value);
        var (unscaledValue, scale) = v1.ToByteArrays();
        if (v1 == BigDecimal.Zero)
        {
            this = Zero;
            return;
        }
        Numerator   = new BigInteger(unscaledValue);
        Denominator = BigInteger.Pow(10, BitConverter.ToInt32(scale, 0));
        Simplify();
    }
    public static bool TryParse(string parse, out BigRational result)
    {
        result = default;
        if (!parse.ContainsOnly("0123456789+-.eE"))
            throw new Exception(
                $"Input value must only contain these '0123456789+-.eE', value'{parse}");
        try
        {
            result = new BigRational(parse);
        }
        catch
        {
            return false;
        }
        return true;
    }
    public BigRational(double value) : this((decimal)value)
    {
    }
    public BigRational(BigDecimal value)
    {
        var bits = value.ToByteArrays();
        if (value == BigDecimal.Zero)
        {
            this = Zero;
            return;
        }
        Numerator   = new BigInteger(bits.unscaledValue);
        Denominator = BigInteger.Pow(10, BitConverter.ToInt32(bits.scale, 0));
        Simplify();
    }
    public BigRational(decimal value)
    {
        var bits = decimal.GetBits(value);
        if (bits                                              == null || bits.Length != 4 ||
            (bits[3] & ~(DecimalSignMask | DecimalScaleMask)) != 0    ||
            (bits[3] & DecimalScaleMask)                      > 28 << 16)
            throw new ArgumentException();
        if (value == decimal.Zero)
        {
            this = Zero;
            return;
        }
        var ul = ((ulong)(uint)bits[2] << 32) | (uint)bits[1];
        Numerator = (new BigInteger(ul) << 32) | (uint)bits[0];
        var isNegative = (bits[3] & DecimalSignMask) != 0;
        if (isNegative)
            Numerator = BigInteger.Negate(Numerator);
        var scale = (bits[3] & DecimalScaleMask) >> 16;
        Denominator = BigInteger.Pow(10, scale);
        Simplify();
    }
    public BigRational(BigInteger numerator, BigInteger denominator)
    {
        if (denominator.Sign == 0)
            throw new DivideByZeroException();
        if (numerator.Sign == 0)
        {
            Numerator   = BigInteger.Zero;
            Denominator = BigInteger.One;
        }
        else if (denominator.Sign < 0)
        {
            Numerator   = BigInteger.Negate(numerator);
            Denominator = BigInteger.Negate(denominator);
        }
        else
        {
            Numerator   = numerator;
            Denominator = denominator;
        }
        Simplify();
    }
    public BigRational(BigInteger whole, BigInteger numerator, BigInteger denominator)
    {
        if (denominator.Sign == 0)
            throw new DivideByZeroException();
        if (numerator.Sign == 0 && whole.Sign == 0)
        {
            Numerator   = BigInteger.Zero;
            Denominator = BigInteger.One;
        }
        else if (denominator.Sign < 0)
        {
            Denominator = BigInteger.Negate(denominator);
            Numerator   = BigInteger.Negate(whole) * Denominator + BigInteger.Negate(numerator);
        }
        else
        {
            Denominator = denominator;
            Numerator   = whole * denominator + numerator;
        }
        Simplify();
    }
    public static BigRational Abs(BigRational r)
    {
        return r.Numerator.Sign < 0
            ? new BigRational(BigInteger.Abs(r.Numerator), r.Denominator)
            : r;
    }
    public static BigRational Negate(BigRational r)
    {
        return new BigRational(BigInteger.Negate(r.Numerator), r.Denominator);
    }
    public static BigRational Invert(BigRational r)
    {
        return new BigRational(r.Denominator, r.Numerator);
    }
    public static BigRational Add(BigRational x, BigRational y)
    {
        return x + y;
    }
    public static BigRational Subtract(BigRational x, BigRational y)
    {
        return x - y;
    }
    public static BigRational Multiply(BigRational x, BigRational y)
    {
        return x * y;
    }
    public static BigRational Divide(BigRational dividend, BigRational divisor)
    {
        return dividend / divisor;
    }
    public static BigRational DivRem(BigRational dividend,
        BigRational                              divisor,
        out BigRational                          remainder)
    {
        var ad = dividend.Numerator   * divisor.Denominator;
        var bc = dividend.Denominator * divisor.Numerator;
        var bd = dividend.Denominator * divisor.Denominator;
        remainder = new BigRational(ad % bc, bd);
        return new BigRational(ad, bc);
    }
    public static BigInteger LeastCommonDenominator(BigRational x, BigRational y)
    {
        return x.Denominator * y.Denominator /
               BigInteger.GreatestCommonDivisor(x.Denominator, y.Denominator);
    }
    public static int Compare(BigRational r1, BigRational r2)
    {
        return BigInteger.Compare(r1.Numerator * r2.Denominator, r2.Numerator * r1.Denominator);
    }
    public static bool operator ==(BigRational x, BigRational y)
    {
        return Compare(x, y) == 0;
    }
    public static bool operator !=(BigRational x, BigRational y)
    {
        return Compare(x, y) != 0;
    }
    public static bool operator <(BigRational x, BigRational y)
    {
        return Compare(x, y) < 0;
    }
    public static bool operator <=(BigRational x, BigRational y)
    {
        return Compare(x, y) <= 0;
    }
    public static bool operator >(BigRational x, BigRational y)
    {
        return Compare(x, y) > 0;
    }
    public static bool operator >=(BigRational x, BigRational y)
    {
        return Compare(x, y) >= 0;
    }
    public static BigRational operator +(BigRational r)
    {
        return r;
    }
    public static BigRational operator -(BigRational r)
    {
        return new BigRational(-r.Numerator, r.Denominator);
    }
    public static BigRational operator ++(BigRational r)
    {
        return r + One;
    }
    public static BigRational operator --(BigRational r)
    {
        return r - One;
    }
    public static BigRational operator +(BigRational r1, BigRational r2)
    {
        return new BigRational(r1.Numerator * r2.Denominator + r1.Denominator * r2.Numerator,
            r1.Denominator * r2.Denominator);
    }
    public static BigRational operator -(BigRational r1, BigRational r2)
    {
        return new BigRational(r1.Numerator * r2.Denominator - r1.Denominator * r2.Numerator,
            r1.Denominator * r2.Denominator);
    }
    public static BigRational operator *(BigRational r1, BigRational r2)
    {
        return new BigRational(r1.Numerator * r2.Numerator, r1.Denominator * r2.Denominator);
    }
    public static BigRational operator /(BigRational r1, BigRational r2)
    {
        return new BigRational(r1.Numerator * r2.Denominator, r1.Denominator * r2.Numerator);
    }
    public static BigRational operator %(BigRational r1, BigRational r2)
    {
        return new BigRational(r1.Numerator * r2.Denominator % (r1.Denominator * r2.Numerator),
            r1.Denominator                                   * r2.Denominator);
    }
    public static explicit operator sbyte(BigRational value)
    {
        return (sbyte)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator ushort(BigRational value)
    {
        return (ushort)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator uint(BigRational value)
    {
        return (uint)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator ulong(BigRational value)
    {
        return (ulong)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator byte(BigRational value)
    {
        return (byte)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator short(BigRational value)
    {
        return (short)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator int(BigRational value)
    {
        return (int)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator long(BigRational value)
    {
        return (long)BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator BigInteger(BigRational value)
    {
        return BigInteger.Divide(value.Numerator, value.Denominator);
    }
    public static explicit operator float(BigRational value)
    {
        return (float)(double)value;
    }
    public static explicit operator double(BigRational value)
    {
        if (SafeCastToDouble(value.Numerator) && SafeCastToDouble(value.Denominator))
            return (double)value.Numerator / (double)value.Denominator;
        var denormalized = value.Numerator * DoublePrecision / value.Denominator;
        if (denormalized.IsZero)
            return value.Sign < 0
                ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000))
                : 0d;
        double result   = 0;
        var    isDouble = false;
        var    scale    = DoubleMaxScale;
        while (scale > 0)
        {
            if (!isDouble)
                if (SafeCastToDouble(denormalized))
                {
                    result   = (double)denormalized;
                    isDouble = true;
                }
                else
                {
                    denormalized = denormalized / 10;
                }
            result = result / 10;
            scale--;
        }
        if (!isDouble)
            return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity;
        return result;
    }
    public static explicit operator BigDecimal(BigRational value)
    {
        var denormalized = value.Numerator * DecimalPrecision / value.Denominator;
        return new BigDecimal(denormalized, (int)DecimalMaxScale);
    }
    public static explicit operator decimal(BigRational value)
    {
        if (SafeCastToDecimal(value.Numerator) && SafeCastToDecimal(value.Denominator))
            return (decimal)value.Numerator / (decimal)value.Denominator;
        var denormalized = value.Numerator * DecimalPrecision / value.Denominator;
        if (denormalized.IsZero)
            return decimal.Zero;
        for (var scale = (int)DecimalMaxScale; scale >= 0; scale--)
            if (!SafeCastToDecimal(denormalized))
            {
                denormalized /= 10;
            }
            else
            {
                var dec = new DecimalUInt32();
                dec.dec   = (decimal)denormalized;
                dec.flags = (dec.flags & ~DecimalScaleMask) | (scale << 16);
                return dec.dec;
            }
        throw new OverflowException();
    }
    public static implicit operator BigRational(sbyte value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(ushort value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(uint value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(ulong value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(byte value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(short value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(int value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(long value)
    {
        return new BigRational((BigInteger)value);
    }
    public static implicit operator BigRational(BigInteger value)
    {
        return new BigRational(value);
    }
    public static implicit operator BigRational(string value)
    {
        return new BigRational(value);
    }
    public static implicit operator BigRational(float value)
    {
        return new BigRational(value);
    }
    public static implicit operator BigRational(double value)
    {
        return new BigRational(value);
    }
    public static implicit operator BigRational(decimal value)
    {
        return new BigRational(value);
    }
    public static implicit operator BigRational(BigDecimal value)
    {
        return new BigRational(value);
    }
    private void Simplify()
    {
        if (Numerator == BigInteger.Zero)
            Denominator = BigInteger.One;
        var gcd = BigInteger.GreatestCommonDivisor(Numerator, Denominator);
        if (gcd > BigInteger.One)
        {
            Numerator   = Numerator   / gcd;
            Denominator = Denominator / gcd;
        }
    }
    private static bool SafeCastToDouble(BigInteger value)
    {
        return DoubleMinValue <= value && value <= DoubleMaxValue;
    }
    private static bool SafeCastToDecimal(BigInteger value)
    {
        return DecimalMinValue <= value && value <= DecimalMaxValue;
    }
    private static void SplitDoubleIntoParts(double dbl,
        out int                                     sign,
        out int                                     exp,
        out ulong                                   man,
        out bool                                    isFinite)
    {
        DoubleUlong du;
        du.uu  = 0;
        du.dbl = dbl;
        sign   = 1 - ((int)(du.uu >> 62) & 2);
        man    = du.uu              & 0x000FFFFFFFFFFFFF;
        exp    = (int)(du.uu >> 52) & 0x7FF;
        if (exp == 0)
        {
            isFinite = true;
            if (man != 0)
                exp = -1074;
        }
        else if (exp == 0x7FF)
        {
            isFinite = false;
            exp      = int.MaxValue;
        }
        else
        {
            isFinite =  true;
            man      |= 0x0010000000000000;
            exp      -= 1075;
        }
    }
    public static double GetDoubleFromParts(int sign, int exp, ulong man)
    {
        DoubleUlong du;
        du.dbl = 0;
        if (man == 0)
        {
            du.uu = 0;
        }
        else
        {
            var cbitShift = CbitHighZero(man) - 11;
            if (cbitShift < 0)
                man >>= -cbitShift;
            else
                man <<= cbitShift;
            exp += 1075;
            if (exp >= 0x7FF)
            {
                du.uu = 0x7FF0000000000000;
            }
            else if (exp <= 0)
            {
                exp--;
                if (exp < -52)
                    du.uu = 0;
                else
                    du.uu = man >> -exp;
            }
            else
            {
                du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong)exp << 52);
            }
        }
        if (sign < 0)
            du.uu |= 0x8000000000000000;
        return du.dbl;
    }
    private static int CbitHighZero(ulong uu)
    {
        if ((uu & 0xFFFFFFFF00000000) == 0)
            return 32 + CbitHighZero((uint)uu);
        return CbitHighZero((uint)(uu >> 32));
    }
    private static int CbitHighZero(uint u)
    {
        if (u == 0)
            return 32;
        var cbit = 0;
        if ((u & 0xFFFF0000) == 0)
        {
            cbit +=  16;
            u    <<= 16;
        }
        if ((u & 0xFF000000) == 0)
        {
            cbit +=  8;
            u    <<= 8;
        }
        if ((u & 0xF0000000) == 0)
        {
            cbit +=  4;
            u    <<= 4;
        }
        if ((u & 0xC0000000) == 0)
        {
            cbit +=  2;
            u    <<= 2;
        }
        if ((u & 0x80000000) == 0)
            cbit += 1;
        return cbit;
    }
    private static (BigRational High, BigRational Low) SqrtLimits(BigInteger number)
    {
        if (number == BigInteger.Zero) return (0, 0);
        var high = number >> 1;
        var low  = BigInteger.Zero;
        while (high > low + 1)
        {
            var n = (high + low) >> 1;
            var p = n * n;
            if (number < p)
                high = n;
            else if (number > p)
                low = n;
            else
                break;
        }
        return (high, low);
    }
    public static BigRational Sqrt(BigRational value)
    {
        if (value == 0) return 0;
        var         hl = SqrtLimits(value.WholePart);
        BigRational n  = 0, p = 0;
        if (hl.High == 0 && hl.Low == 0)
            return 0;
        var high = hl.High;
        var low  = hl.Low;
        var d    = DecimalPrecision;
        var pp   = 1 / (BigRational)d;
        while (high > low + pp)
        {
            n = (high + low) / 2;
            p = n            * n;
            if (value < p)
                high = n;
            else if (value > p)
                low = n;
            else
                break;
        }
        var r = value == p ? n : low;
        return r;
    }
    public BigRational Sqrt()
    {
        return Sqrt(this);
    }
    public static BigRational ArcTangent(BigRational v, int n)
    {
        var retVal = v;
        for (var i = 1; i < n; i++)
        {
            var powRat = Pow(v, 2 * i + 1);
            retVal += new BigRational(powRat.Numerator * (BigInteger)Math.Pow(-1d, i),
                (2 * i + 1)                            * powRat.Denominator);
        }
        return retVal;
    }
    public static BigRational Reciprocal(BigRational v)
    {
        return new BigRational(v.Denominator, v.Numerator);
    }
    public static BigRational Round(BigRational number, int decimalPlaces)
    {
        BigRational power = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
    }
    public void Round(int decimalPlaces)
    {
        var         number = this;
        BigRational power  = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
        Numerator   = n.Numerator;
        Denominator = n.Denominator;
    }
    public static BigRational Pow(BigRational v, int e)
    {
        if (e < 1) throw new ArgumentException("Powers must be greater than or equal to one.");
        var retVal = new BigRational(v.Numerator, v.Denominator);
        for (var i = 1; i < e; i++)
        {
            retVal.Numerator   *= v.Numerator;
            retVal.Denominator *= v.Denominator;
        }
        return retVal;
    }
    public static BigRational Min(BigRational r, BigRational l)
    {
        return l < r ? l : r;
    }
    public static BigRational Max(BigRational r, BigRational l)
    {
        return l > r ? l : r;
    }
    /// <summary>
    ///     Set Pi before call
    /// </summary>
    public static BigRational ToRadians(BigRational degrees)
    {
        return degrees * Pi / 180;
    }
    /// <summary>
    ///     Set Pi before call
    /// </summary>
    public static BigRational ToDegrees(BigRational rads)
    {
        return rads * 180 / Pi;
    }
    private static BigRational Factorial(BigRational x)
    {
        BigRational r = 1;
        BigRational c = 1;
        while (c <= x)
        {
            r *= c;
            c++;
        }
        return r;
    }
    public static BigRational Exp(BigRational x)
    {
        BigRational r  = 0;
        BigRational r1 = 0;
        var         k  = 0;
        while (true)
        {
            r += Pow(x, k) / Factorial(k);
            if (r == r1)
                break;
            r1 = r;
            k++;
        }
        return r;
    }
    public static BigRational Sine(BigRational ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigRational[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigRational();
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        var sin = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= Factorials[i * 2];
            if ((i & 1) == 1)
                sin -= trm;
            else
                sin += trm;
        }
        return sin;
    }
    public static BigRational Atan(BigRational ar, int n)
    {
        var atan = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= i * 2;
            if ((i & 1) == 1)
                atan -= trm;
            else
                atan += trm;
        }
        return atan;
    }
    public static BigRational Cosine(BigRational ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigRational[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigRational();
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        BigRational cos = 1.0;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2);
            trm /= Factorials[i * 2 - 1];
            if ((i & 1) == 1)
                cos -= trm;
            else
                cos += trm;
        }
        return cos;
    }
    public static BigRational Tangent(BigRational ar, int n)
    {
        return Sine(ar, n) / Cosine(ar, n);
    }
    public static BigRational CoTangent(BigRational ar, int n)
    {
        return Cosine(ar, n) / Sine(ar, n);
    }
    public static BigRational Secant(BigRational ar, int n)
    {
        return 1.0 / Cosine(ar, n);
    }
    public static BigRational CoSecant(BigRational ar, int n)
    {
        return 1.0 / Sine(ar, n);
    }
    private static BigRational GetE(int n)
    {
        BigRational e = 1.0;
        var         c = n;
        while (c > 0)
        {
            BigRational f = 0;
            if (c == 1)
            {
                f = 1;
            }
            else
            {
                var i = c - 1;
                f = c;
                while (i > 0)
                {
                    f *= i;
                    i--;
                }
            }
            c--;
            e += 1.0 / f;
        }
        return e;
    }
    public static BigRational NthRoot(BigRational value, int nth)
    {
        BigRational lx;
        var         a = value;
        var         n = nth;
        BigRational s = 1.0;
        do
        {
            var t = s;
            lx = a / Pow(s, n - 1);
            var r = (n        - 1) * s;
            s = (lx + r) / n;
        } while (lx != s);
        return s;
    }
    public static BigRational LogN(BigRational value)
    {
        BigRational a;
        var         p = value;
        BigRational n = 0.0;
        while (p >= E)
        {
            p /= E;
            n++;
        }
        n += p / E;
        p =  value;
        do
        {
            a = n;
            var lx = p         / Exp(n - 1.0);
            var r  = (n - 1.0) * E;
            n = (lx + r) / E;
        } while (n != a);
        return n;
    }
    public static BigRational Log(BigRational n, int b)
    {
        return LogN(n) / LogN(b);
    }
    private static int ConversionIterations(BigRational v)
    {
        return (int)((DecimalMaxScale + 1) / (2 * Math.Log10((double)Reciprocal(v))));
    }
    public static BigRational GetPI()
    {
        var oneFifth         = new BigRational(1, 5);
        var oneTwoThirtyNine = new BigRational(1, 239);
        var arcTanOneFifth   = ArcTangent(oneFifth, ConversionIterations(oneFifth));
        var arcTanOneTwoThirtyNine =
            ArcTangent(oneTwoThirtyNine, ConversionIterations(oneTwoThirtyNine));
        return arcTanOneFifth * 16 - arcTanOneTwoThirtyNine * 4;
    }
    public override string ToString()
    {
        var ret = new StringBuilder();
        ret.Append(Numerator.ToString("R", CultureInfo.InvariantCulture));
        ret.Append(Solidus);
        ret.Append(Denominator.ToString("R", CultureInfo.InvariantCulture));
        return ret.ToString();
    }
}

(Obsolete) IntX.cs

Adjustable Bit Width Integer 32,64,128,256,512,1024,2048…

Why not just use BigInteger? Think Hashing which relies on overflow.

Dec-03, 2020: Added ToBinaryString, ToOctalString, Pow, ModPow, GetSign, GetDataUsed, UsedData, BitWidth, GetDecimalPlaces, TwosComplement, Pow, Log, GetFactors, GreatestCommonDivisor, LeastCommonMultiple, Log10, LogN, Sqrt, and ConstructFromArray.

Jun-11,2021: Obsolete Use xIntX Instead.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(IntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable
{
private const int DataSize = sizeof(uint);
private const uint AllBits = ~(uint) 0;
private const int DataSizeBits = sizeof(uint) * 8;
private const uint HiNeg = (uint) 1 << (DataSizeBits - 1);
private static int _dataBitWidth;
private static int DataLength;
public static readonly IntX One = new IntX(1, 32);
public static readonly IntX Two = new IntX(2, 32);
public static readonly IntX Zero = new IntX(0, 32);
public static readonly IntX Ten = new IntX(10, 32);
public static readonly IntX Three = new IntX(3, 32);
private readonly SerializationInfo SInfo;
public uint[] Data;
public IntX(IntX value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
value.Data.CopyTo(Data, 0);
}
public IntX(string value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
if (!TryParse(value, out var result))
throw new Exception("TryParse Failed.");
Data = new uint[DataLength];
result.Data.CopyTo(Data, 0);
}
public IntX(byte value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(bool value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = (uint) (value ? 1 : 0);
}
public IntX(char value, int bitLength)
{
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(BigDecimal value, int bitLength)
{
var ba = value.WholePart.ToByteArray();
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var len = ba.Length / DataSize;
Data = new uint[DataLength];
for (var i = 0; i < Data.Length; i++)
Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
}
public IntX(decimal value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-value, DataBitWidth);
n.Data.CopyTo(Data, 0);
return;
}
var bits = decimal.GetBits(value);
Data[2] = (uint) bits[2];
Data[1] = (uint) bits[1];
Data[0] = (uint) bits[0];
}
public IntX(double value, int bitLength) : this((decimal) value, bitLength)
{
}
public IntX(float value, int bitLength) : this((decimal) value, bitLength)
{
}
public IntX(short value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(int value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(long value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[1] = (uint) ((value >> 32) & 0xffffffff);
Data[0] = (uint) (value & 0xffffffff);
}
public IntX(sbyte value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
if (value < 0)
{
var n = -new IntX(-(value + 1), DataBitWidth) - 1;
n.Data.CopyTo(Data, 0);
return;
}
Data[0] = (uint) value;
}
public IntX(BigRational value, int bitLength)
{
value.Round(1);
ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
}
public IntX(ushort value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(uint value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[0] = value;
}
public IntX(ulong value, int bitLength)
{
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
Data[1] = (uint) ((value >> 32) & 0xffffffff);
Data[0] = (uint) (value & 0xffffffff);
}
public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
{
}
public IntX(byte[] value, int bitLength)
{
var minSize = value.Length / DataSize;
if (value == null)
throw new ArgumentNullException("value");
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var byteCount = value.Length;
var isNegative = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
var unalignedBytes = byteCount % DataSize;
var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
Data = new uint[Math.Max(dwordCount, DataLength)];
if (byteCount == 0)
return;
int curDword, curByte, byteInDword;
curByte = 3;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
byteInDword = 0;
while (byteInDword < DataSize)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
curByte--;
byteInDword++;
}
curByte += 8;
}
if (unalignedBytes != 0)
{
if (isNegative)
Data[dwordCount - 1] = 0xffffffff;
for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
}
}
}
public IntX(int sign, uint[] array, int bitLength)
{
if (array == null)
throw new Exception("Array cannot be null.");
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
Data = new uint[DataLength];
var ba = new byte[DataSize];
for (var i = 0; i < Math.Min(DataLength, array.Length); i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
if (sign < 0)
Data[DataLength - 1] |= HiNeg;
else
Data[DataLength - 1] &= ~HiNeg;
}
public IntX(uint[] array, int bitLength)
{
if (array == null)
throw new Exception("Array cannot be null.");
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
if (array.Length != DataLength)
Array.Resize(ref array, DataLength);
Data = new uint[DataLength];
var ba = new byte[DataSize];
for (var i = 0; i < Data.Length; i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
}
protected IntX(SerializationInfo info, StreamingContext context)
{
SInfo = info;
}
private static int DataBitWidth
{
get => _dataBitWidth;
set
{
if (value < 32)
throw new Exception($"Data Bit Width {value} must not be less than 32.");
_dataBitWidth = value;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DDisplay => ToString();
public IntX MaxValue
{
get
{
var r = new IntX(0, DataBitWidth);
for (var i = 0; i < r.Data.Length; ++i)
r.Data[i] = uint.MaxValue;
r.Data[r.Data.Length - 1] = int.MaxValue;
return r;
}
}
public int BitWidth
{
get
{
IntX bw = 1;
var v = new IntX(this, DataBitWidth);
while ((v >>= 1) > 0)
bw++;
if (bw < 8)
bw = 8;
while (bw % 8 != 0)
bw++;
return (int) bw;
}
}
public int Sign
{
get
{
var allZero = true;
var ba = Data;
for (var i = 0; i < ba.Length; i++)
if (ba[i] != 0)
{
allZero = false;
break;
}
if (allZero)
return 0;
return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1;
}
}
public bool IsOne => this == 1;
public bool IsEven => (this & 1) == 0;
public bool IsNegative => Sign < 0;
public bool IsZero
{
get
{
for (var i = 0; i < Data.Length; i++)
if (Data[i] != 0)
return false;
return true;
}
}
public int DataUsed
{
get
{
var DataUsed = Data.Length;
if (!IsNegative)
{
while (DataUsed > 1 && Data[DataUsed - 1] == 0)
--DataUsed;
if (DataUsed == 0)
DataUsed = 1;
}
return DataUsed;
}
}
int IComparable.CompareTo(object obj)
{
return Compare(this, obj);
}
public int CompareTo(IntX value)
{
return Compare(this, value);
}
TypeCode IConvertible.GetTypeCode()
{
return TypeCode.Object;
}
bool IConvertible.ToBoolean(IFormatProvider provider)
{
return (bool) this;
}
byte IConvertible.ToByte(IFormatProvider provider)
{
return (byte) this;
}
char IConvertible.ToChar(IFormatProvider provider)
{
return (char) this;
}
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
throw new InvalidCastException();
}
decimal IConvertible.ToDecimal(IFormatProvider provider)
{
return (decimal) this;
}
double IConvertible.ToDouble(IFormatProvider provider)
{
return (double) this;
}
short IConvertible.ToInt16(IFormatProvider provider)
{
return (short) this;
}
int IConvertible.ToInt32(IFormatProvider provider)
{
return (int) this;
}
long IConvertible.ToInt64(IFormatProvider provider)
{
return (long) this;
}
sbyte IConvertible.ToSByte(IFormatProvider provider)
{
return (sbyte) this;
}
float IConvertible.ToSingle(IFormatProvider provider)
{
return (float) this;
}
string IConvertible.ToString(IFormatProvider provider)
{
return ToString(null, provider);
}
public object ToType(Type conversionType, IFormatProvider provider)
{
object value;
if (TryConvert(conversionType, provider, out value))
return value;
throw new InvalidCastException();
}
ushort IConvertible.ToUInt16(IFormatProvider provider)
{
if (Data[1] != 0)
throw new OverflowException();
return Convert.ToUInt16(Data[0]);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
if (Data[1] != 0)
throw new OverflowException();
return Convert.ToUInt32(Data[0]);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
if (Data[1] != 0)
return ((ulong) Data[1] << 32) | Data[0];
return Data[0];
}
public bool Equals(IntX obj)
{
if (ReferenceEquals(obj, null))
return false;
if (ReferenceEquals(this, obj))
return true;
if (Data.Length != obj.Data.Length)
return false;
if (Sign != obj.Sign)
return false;
for (var i = 0; i < Data.Length; i++)
if (Data[i] != obj.Data[i])
return false;
return true;
}
public string ToString(string format, IFormatProvider formatProvider)
{
if (formatProvider == null)
formatProvider = CultureInfo.CurrentCulture;
if (!string.IsNullOrEmpty(format))
{
var ch = format[0];
if (ch == 'x' || ch == 'X')
{
int.TryParse(format.Substring(1).Trim(), out var min);
return ToHexString(ch == 'X');
}
if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
throw new NotSupportedException("Not supported format: " + format);
}
return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Bits", DataBitWidth);
info.AddValue("Data", Data, typeof(uint[]));
}
public void OnDeserialization(object sender)
{
if (SInfo == null)
return;
DataBitWidth = SInfo.GetInt32("Bits");
if (DataBitWidth != 0)
{
DataLength = DataBitWidth >> 5;
var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
if (array == null)
throw new Exception("Array cannot be null.");
if (array.Length != DataLength)
Array.Resize(ref array, DataLength);
Data = new uint[DataLength];
var ba = new byte[4];
for (var i = 0; i < DataLength; i++)
{
Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
Data[i] = BitConverter.ToUInt32(ba, 0);
}
}
}
private static byte[] ToByteArray(ulong[] value)
{
var ba = new byte[value.Length << 3];
Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
return ba;
}
private static byte[] ToByteArray(uint[] value)
{
var ba = new byte[value.Length << 2];
Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
return ba;
}
public override int GetHashCode()
{
var s = Sign;
var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5;
for (var i = 0; i < Data.Length; i++)
{
hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i];
hash *= 0x1000193;
}
return (int) hash;
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override string ToString()
{
return ToString(null, null);
}
public string ToString(string format)
{
return ToString(format, null);
}
public string ToHexString(bool caps)
{
var bytes = ToByteArray().Invert();
var sb = new StringBuilder();
var x = caps ? "X" : "x";
foreach (var b in bytes)
{
var hex = b.ToString($"{x}2");
sb.Append(hex);
}
return sb.ToString();
}
private string ToString(NumberFormatInfo info, int radix)
{
if (radix < 2 || radix > 36)
throw new ArgumentOutOfRangeException("radix");
if (Sign == 0)
return "0";
var negative = Sign < 0;
var a = new IntX(this, DataBitWidth);
if (negative)
try
{
a = -a;
}
catch (Exception ex)
{
}
var biRadix = new IntX(radix, DataBitWidth);
const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
var al = new ArrayList();
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
al.Insert(0, charSet[(int) remainder.Data[0]]);
a = quotient;
}
catch (Exception ex)
{
break;
}
var result = new string((char[]) al.ToArray(typeof(char)));
if (radix == 10 && negative)
return "-" + result;
return result;
}
public static IntX Abs(IntX value)
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException("value");
if (value.Sign < 0)
return -value;
return value;
}
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 IntX Parse(string value)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
public static IntX Parse(string value, NumberStyles style)
{
return Parse(value, style, NumberFormatInfo.CurrentInfo);
}
public static IntX Parse(string value, IFormatProvider provider)
{
return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
}
public static IntX 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 IntX result)
{
return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX result)
{
result = 0;
if (string.IsNullOrEmpty(value))
return false;
if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(1);
}
else
{
if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(2);
}
}
if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
return TryParseNum(value, 16, out result);
return TryParseNum(value, 10, out result);
}
public static bool TryParseNum(string digits, int radix, out IntX result)
{
result = new IntX(0, DataBitWidth);
if (digits == null)
return false;
var multiplier = new IntX(1, DataBitWidth);
digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
var nDigits = digits[0] == '-' ? 1 : 0;
for (var idx = digits.Length - 1; idx >= nDigits; idx--)
{
var d = (int) digits[idx];
if (d >= '0' && d <= '9')
d -= '0';
else if (d >= 'A' && d <= 'Z')
d = d - 'A' + 10;
else
return false;
if (d >= radix)
return false;
result += multiplier * d;
multiplier *= radix;
if (multiplier.DataUsed == DataLength)
throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
}
if (digits[0] == '-')
result = -result;
return true;
}
public static int Compare(IntX left, object right)
{
if (right is IntX)
return Compare(left, (IntX) right);
if (right is bool)
return Compare(left, new IntX((bool) right, DataBitWidth));
if (right is byte)
return Compare(left, new IntX((byte) right, DataBitWidth));
if (right is char)
return Compare(left, new IntX((char) right, DataBitWidth));
if (right is decimal)
return Compare(left, new IntX((decimal) right, DataBitWidth));
if (right is double)
return Compare(left, new IntX((double) right, DataBitWidth));
if (right is short)
return Compare(left, new IntX((short) right, DataBitWidth));
if (right is int)
return Compare(left, new IntX((int) right, DataBitWidth));
if (right is long)
return Compare(left, new IntX((long) right, DataBitWidth));
if (right is sbyte)
return Compare(left, new IntX((sbyte) right, DataBitWidth));
if (right is float)
return Compare(left, new IntX((float) right, DataBitWidth));
if (right is ushort)
return Compare(left, new IntX((ushort) right, DataBitWidth));
if (right is uint)
return Compare(left, new IntX((uint) right, DataBitWidth));
if (right is ulong)
return Compare(left, new IntX((ulong) right, DataBitWidth));
var bytes = right as byte[];
if (bytes != null)
return Compare(left, new IntX(bytes, DataBitWidth));
if (right is Guid)
return Compare(left, new IntX((Guid) right, DataBitWidth));
throw new ArgumentException();
}
public static int Compare(IntX left, IntX right)
{
if (ReferenceEquals(left, right))
return 0;
if (left.Sign >= 0 && right.Sign < 0)
return 1;
if (left.Sign < 0 && right.Sign >= 0)
return -1;
if (left.Data.Length != right.Data.Length)
return -1;
for (var i = left.Data.Length - 1; i > 0; i--)
if (left.Data[i] != right.Data[i])
return left.Data[i].CompareTo(right.Data[i]);
return left.Data[0].CompareTo(right.Data[0]);
}
public static implicit operator IntX(bool value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(byte value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(char value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(decimal value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(double value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(short value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(int value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(long value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(sbyte value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator IntX(float value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(ushort value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(uint value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(ulong value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigInteger value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigRational value)
{
return new IntX(value, DataBitWidth);
}
public static implicit operator IntX(BigDecimal value)
{
return new IntX(value, DataBitWidth);
}
public static explicit operator bool(IntX value)
{
return (byte) value.Data[0] != 0;
}
public static explicit operator byte(IntX value)
{
return (byte) value.Data[0];
}
public static explicit operator char(IntX value)
{
return (char) (ushort) value.Data[0];
}
public static explicit operator decimal(IntX value)
{
if (value.Sign == 0)
return 0;
if (value.Data.Length == 1)
return new decimal((int) value.Data[0], 0, 0, value.Sign < 0, 0);
if (value.Data.Length == 2)
return new decimal((int) value.Data[0], (int) value.Data[1], 0, value.Sign < 0, 0);
if (value.Data.Length == 3)
return new decimal((int) value.Data[0], (int) value.Data[1], (int) value.Data[2], value.Sign < 0, 0);
throw new ArgumentException("Value length exceeds decimal length.");
}
public static explicit operator double(IntX value)
{
if (value.Sign == 0)
return 0;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
throw new OverflowException();
return d;
}
public static explicit operator float(IntX value)
{
if (value.Sign == 0)
return 0;
var nfi = CultureInfo.InvariantCulture.NumberFormat;
if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
throw new OverflowException();
return f;
}
public static explicit operator short(IntX value)
{
if (value.Data[0] > 0x8000)
throw new OverflowException();
if (value.Data[0] == 0x8000 && value.Sign > 0)
throw new OverflowException();
return (short) ((int) value.Data[0] * value.Sign);
}
public static explicit operator int(IntX value)
{
if (value.Sign == 0)
return 0;
return (int) value.Data[0] * value.Sign;
}
public static explicit operator long(IntX value)
{
if (value.Sign == 0)
return 0;
if (value.Data[0] > int.MaxValue)
throw new OverflowException();
if (value.Data.Length > 1)
if (value.Data[1] != 0)
return (long) (((ulong) value.Data[1] << 32) | value.Data[0]) * value.Sign;
return value.Data[0] * value.Sign;
}
public static explicit operator uint(IntX value)
{
if (value.Sign == 0)
return 0;
return value.Data[0];
}
public static explicit operator ushort(IntX value)
{
if (value.Sign == 0)
return 0;
return (ushort) value.Data[0];
}
public static explicit operator ulong(IntX value)
{
if (value.Data.Length > 1)
if (value.Data[1] != 0)
return ((ulong) value.Data[1] << 32) | value.Data[0];
return value.Data[0];
}
public static explicit operator BigInteger(IntX value)
{
return new BigInteger(value.ToByteArray());
}
public static explicit operator BigRational(IntX value)
{
return new BigRational(new BigInteger(value.ToByteArray()));
}
public static bool operator >(IntX left, IntX right)
{
return left.CompareTo(right) > 0;
}
public static bool operator <(IntX left, IntX right)
{
return Compare(left, right) < 0;
}
public static bool operator >=(IntX left, IntX right)
{
return Compare(left, right) >= 0;
}
public static bool operator <=(IntX left, IntX right)
{
return Compare(left, right) <= 0;
}
public static bool operator !=(IntX left, IntX right)
{
return !left.Equals(right);
}
public static bool operator ==(IntX left, IntX right)
{
return left.Equals(right);
}
public static IntX operator +(IntX value)
{
return value;
}
public static IntX operator ~(IntX value)
{
var da = new uint[DataLength];
for (var idx = 0; idx < DataLength; idx++)
da[idx] = ~value.Data[idx];
return new IntX(da, DataBitWidth);
}
public static IntX operator -(IntX value)
{
if (ReferenceEquals(value, null))
throw new ArgumentNullException("value");
if (value.IsZero)
return Zero;
var da = new uint[DataLength];
for (var i = 0; i < da.Length; i++)
da[i] = ~value.Data[i];
var carry = true;
var index = 0;
while (carry && index < da.Length)
{
var val = (long) da[index] + 1;
da[index] = (uint) (val & AllBits);
carry = val >> DataSizeBits > 0;
index++;
}
return new IntX(da, DataBitWidth);
}
public static IntX operator ++(IntX value)
{
return value + 1;
}
public static IntX operator --(IntX value)
{
return value - 1;
}
public static IntX Negate(IntX value)
{
var ldata = (uint[]) value.Data.Clone();
for (var i = 0; i < value.Data.Length; i++)
ldata[i] = ~value.Data[i];
return new IntX(value.Sign, ldata, DataBitWidth);
}
public static IntX operator +(IntX left, IntX right)
{
if (right.IsZero)
return left;
if (left.IsZero)
return right;
var dl = Math.Max(left.Data.Length, right.Data.Length);
var lim = Math.Min(left.Data.Length, right.Data.Length);
var result = new uint[dl];
long carry = 0;
for (var i = 0; i < dl && i < lim; i++)
{
var sum = left.Data[i] + (long) right.Data[i] + carry;
carry = sum >> 32;
result[i] = (uint) (sum & 0xFFFFFFFF);
}
if (carry != 0)
{
var idx = 0;
while (idx < result.Length - 1)
{
if (result[idx] == 0)
break;
idx++;
}
result[idx] = (uint) carry;
}
return new IntX(left.Sign * right.Sign, result, DataBitWidth);
}
public static IntX operator -(IntX left, IntX right)
{
if (right.IsZero)
return left;
if (left.IsZero)
return -right;
var size = Math.Max(left.Data.Length, right.Data.Length) + 1;
var da = new uint[size];
long carry = 0;
for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++)
{
var diff = left.Data[i] - (long) right.Data[i] - carry;
da[i] = (uint) (diff & AllBits);
carry = diff < 0 ? 1 : 0;
}
return new IntX(da, DataBitWidth);
}
public static IntX Add(IntX left, IntX right)
{
return left + right;
}
public static IntX Subtract(IntX left, IntX right)
{
return left - right;
}
public static IntX Divide(IntX dividend, IntX divisor)
{
if (divisor == 0)
throw new DivideByZeroException();
return DivRem(dividend, divisor, out var integer);
}
public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
remainder = new IntX(1, rem, DataBitWidth);
quotient = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth);
}
public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder)
{
if (divisor == 0)
throw new DivideByZeroException();
DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
remainder = new IntX(1, rem, DataBitWidth);
return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth);
}
private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
{
const ulong hiBit = 0x100000000;
var divisorLen = GetLength(divisor);
var dividendLen = GetLength(dividend);
if (divisorLen <= 1)
{
ulong rem = 0;
var div = divisor[0];
quotient = new uint[dividendLen];
remainder = new uint[1];
for (var i = dividendLen - 1; i >= 0; i--)
{
rem *= hiBit;
rem += dividend[i];
var q = rem / div;
rem -= q * div;
quotient[i] = (uint) q;
}
remainder[0] = (uint) rem;
return;
}
if (dividendLen >= divisorLen)
{
var shift = GetNormalizeShift(divisor[divisorLen - 1]);
var normDividend = new uint[dividendLen + 1];
var normDivisor = new uint[divisorLen];
Normalize(dividend, dividendLen, normDividend, shift);
Normalize(divisor, divisorLen, normDivisor, shift);
quotient = new uint[dividendLen - divisorLen + 1];
for (var j = dividendLen - divisorLen; j >= 0; j--)
{
var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
var qj = dx / normDivisor[divisorLen - 1];
dx -= qj * normDivisor[divisorLen - 1];
do
{
if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
break;
qj -= 1L;
dx += normDivisor[divisorLen - 1];
} while (dx < hiBit);
ulong di = 0;
ulong dj;
var index = 0;
while (index < divisorLen)
{
var dqj = normDivisor[index] * qj;
dj = normDividend[index + j] - (uint) dqj - di;
normDividend[index + j] = (uint) dj;
dqj = dqj >> 32;
dj = dj >> 32;
di = dqj - dj;
index++;
}
dj = normDividend[j + divisorLen] - di;
normDividend[j + divisorLen] = (uint) dj;
quotient[j] = (uint) qj;
if ((long) dj < 0)
{
quotient[j]--;
ulong sum = 0;
for (index = 0; index < divisorLen; index++)
{
sum = normDivisor[index] + normDividend[j + index] + sum;
normDividend[j + index] = (uint) sum;
sum = sum >> 32;
}
sum += normDividend[j + divisorLen];
normDividend[j + divisorLen] = (uint) sum;
}
}
remainder = Unnormalize(normDividend, shift);
return;
}
quotient = new uint[1];
remainder = dividend;
}
private static int GetLength(uint[] uints)
{
var index = uints.Length - 1;
while (index >= 0 && uints[index] == 0)
index--;
return index + 1;
}
private static int GetNormalizeShift(uint ui)
{
var shift = 0;
if ((ui & 0xffff0000) == 0)
{
ui = ui << 16;
shift += 16;
}
if ((ui & 0xff000000) == 0)
{
ui = ui << 8;
shift += 8;
}
if ((ui & 0xf0000000) == 0)
{
ui = ui << 4;
shift += 4;
}
if ((ui & 0xc0000000) == 0)
{
ui = ui << 2;
shift += 2;
}
if ((ui & 0x80000000) == 0)
shift++;
return shift;
}
private static uint[] Unnormalize(uint[] normalized, int shift)
{
var len = GetLength(normalized);
var unnormalized = new uint[len];
if (shift > 0)
{
var rshift = 32 - shift;
uint r = 0;
for (var i = len - 1; i >= 0; i--)
{
unnormalized[i] = (normalized[i] >> shift) | r;
r = normalized[i] << rshift;
}
}
else
{
for (var j = 0; j < len; j++)
unnormalized[j] = normalized[j];
}
return unnormalized;
}
private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
{
int i;
uint n = 0;
if (shift > 0)
{
var rShift = 32 - shift;
for (i = 0; i < len; i++)
{
normalized[i] = (unormalized[i] << shift) | n;
n = unormalized[i] >> rShift;
}
}
else
{
i = 0;
while (i < len)
{
normalized[i] = unormalized[i];
i++;
}
}
while (i < normalized.Length)
normalized[i++] = 0;
if (n != 0)
normalized[len] = n;
}
public static IntX Remainder(IntX dividend, IntX divisor)
{
DivRem(dividend, divisor, out var remainder);
return remainder;
}
public static IntX Max(IntX left, IntX right)
{
return left.CompareTo(right) < 0 ? right : left;
}
public static IntX Min(IntX left, IntX right)
{
return left.CompareTo(right) <= 0 ? left : right;
}
public static IntX operator %(IntX dividend, IntX divisor)
{
return Remainder(dividend, divisor);
}
public static IntX operator /(IntX dividend, IntX divisor)
{
return Divide(dividend, divisor);
}
public ulong[] ToUIn64Array()
{
var al = Data.Length >> 1;
if (al * 2 != Data.Length)
al++;
var arr = new ulong[al];
Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2);
return arr;
}
public uint[] ToUIn32Array()
{
return Data;
}
public byte[] ToByteArray()
{
var ba = new byte[Data.Length * DataSize];
Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize);
return ba;
}
private void TrimToMsb()
{
var dataUsed = Data.Length;
while (dataUsed > 1 && Data[dataUsed - 1] == 0)
--dataUsed;
if (dataUsed != Data.Length)
{
var tData = new uint[dataUsed];
for (var i = 0; i < dataUsed; i++)
tData[i] = Data[i];
Data = (uint[]) tData.Clone();
}
}
public static IntX Multiply(IntX left, IntX right)
{
if (left == 0 || right == 0)
return Zero;
if (left == 1 && right != 1)
return right;
if (left != 1 && right == 1)
return left;
if (left == 1 && right == 1)
return One;
var xInts = left.Data;
var yInts = right.Data;
var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
for (var i = 0; i < xInts.Length; i++)
{
var index = i;
ulong remainder = 0;
foreach (var yi in yInts)
{
remainder = remainder + (ulong) xInts[i] * yi + mulInts[index];
mulInts[index++] = (uint) remainder;
remainder = remainder >> 32;
}
while (remainder != 0)
{
remainder += mulInts[index];
mulInts[index++] = (uint) remainder;
remainder = remainder >> 32;
}
}
return new IntX(left.Sign * right.Sign, mulInts, DataBitWidth);
}
public static IntX operator *(IntX left, IntX right)
{
return Multiply(left, right);
}
public static IntX operator >>(IntX value, int shift)
{
if (shift == 0)
return value;
if (shift == int.MinValue)
return value << int.MaxValue << 1;
if (shift < 0)
return value << -shift;
var xd = value.Data;
var shiftAmount = 32;
var invShift = 0;
var bufLen = xd.Length;
while (bufLen > 1 && xd[bufLen - 1] == 0)
bufLen--;
for (var count = shift; count > 0; count -= shiftAmount)
{
if (count < shiftAmount)
{
shiftAmount = count;
invShift = 32 - shiftAmount;
}
ulong carry = 0;
for (var i = bufLen - 1; i >= 0; i--)
{
var val = (ulong) xd[i] >> shiftAmount;
val |= carry;
carry = (ulong) xd[i] << invShift;
xd[i] = (uint) val;
}
}
return new IntX(value.Sign, xd, DataBitWidth);
}
public static IntX operator <<(IntX value, int shift)
{
if (shift == 0)
return value;
if (shift == int.MinValue)
return value >> int.MaxValue >> 1;
if (shift < 0)
return value >> -shift;
var digitShift = shift / 32;
var smallShift = shift - digitShift * 32;
var xd = value.Data;
var xl = xd.Length;
var zd = new uint[xl + digitShift + 1];
if (smallShift == 0)
{
for (var index = 0; index < xl; ++index)
zd[index + digitShift] = xd[index];
}
else
{
var carryShift = 32 - smallShift;
uint carry = 0;
int index;
for (index = 0; index < xl; ++index)
{
var rot = xd[index];
zd[index + digitShift] = (rot << smallShift) | carry;
carry = rot >> carryShift;
}
zd[index + digitShift] = carry;
}
return new IntX(value.Sign, zd, DataBitWidth);
}
public static IntX operator |(IntX left, IntX right)
{
if (left == 0)
return right;
if (right == 0)
return left;
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu | yu;
}
return new IntX(left.Sign * right.Sign, z, DataBitWidth);
}
public static IntX operator ^(IntX left, IntX right)
{
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu ^ yu;
}
return new IntX(left.Sign * right.Sign, z, DataBitWidth);
}
public static IntX operator &(IntX left, IntX right)
{
if (left == 0 || right == 0)
return 0;
var z = new uint[Math.Max(left.Data.Length, right.Data.Length)];
var lExt = left.Sign < 0 ? uint.MaxValue : 0U;
var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
for (var i = 0; i < z.Length; i++)
{
var xu = i < left.Data.Length ? left.Data[i] : lExt;
var yu = i < right.Data.Length ? right.Data[i] : rExt;
z[i] = xu & yu;
}
return new IntX(left.Sign * right.Sign, z, DataBitWidth);
}
public string ToBinaryString()
{
var bytes = ToByteArray();
var index = bytes.Length - 1;
var base2 = new StringBuilder(bytes.Length * 8);
var binary = Convert.ToString(bytes[index], 2);
if (binary[0] != '0' && Sign == 1) base2.Append('0');
base2.Append(binary);
for (index--; index >= 0; index--)
base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
return base2.ToString();
}
public string ToOctalString()
{
var bytes = ToByteArray();
var index = bytes.Length - 1;
var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8);
var rem = bytes.Length % 3;
if (rem == 0) rem = 3;
var base0 = 0;
while (rem != 0)
{
base0 <<= 8;
base0 += bytes[index--];
rem--;
}
var octal = Convert.ToString(base0, 8);
if (octal[0] != '0' && Sign == 1) base8.Append('0');
base8.Append(octal);
while (index >= 0)
{
base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
index -= 3;
}
return base8.ToString();
}
public static IntX Pow(IntX value, IntX exponent, int bitLength)
{
if (value == null)
throw new ArgumentNullException("Value cannot be null");
if (exponent == null)
throw new ArgumentNullException("Exponent cannot be null");
if (exponent < 0)
throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative");
var result = new IntX("1", bitLength);
while (exponent != 0)
{
if ((exponent & 1) != 0)
result *= value;
exponent >>= 1;
value *= value;
}
return result;
}
public static IntX ModPow(IntX n, IntX e, IntX m)
{
var n1 = new IntX(n, DataBitWidth);
var e1 = new IntX(e, DataBitWidth);
var r = new IntX(1, DataBitWidth);
while (e1 != 0)
{
if (e1 % 2 == 1)
r = r * n1 % m;
e1 >>= 1;
n1 = n1 * n1 % m;
}
return r;
}
public static int GetSign(uint[] value)
{
var allZero = true;
for (var i = 0; i < value.Length; i++)
if (value[i] != 0)
{
allZero = false;
break;
}
if (allZero)
return 0;
return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1;
}
private static int GetDataUsed(uint[] array)
{
var neg = GetSign(array) < 0;
var dataUsed = array.Length;
if (!neg)
{
while (dataUsed > 1 && array[dataUsed - 1] == 0)
--dataUsed;
if (dataUsed == 0)
dataUsed = 1;
}
return dataUsed;
}
public int GetDecimalPlaces()
{
var dPlaces = 0;
if (Sign == 0)
return 1;
var a = new IntX(this, DataBitWidth);
if (Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new IntX(10, DataBitWidth);
while (a > 0)
try
{
Divide(a, biRadix, out var remainder, out var quotient);
a = quotient;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
public (IntX approximateRoot, BigRational realRoot) Sqrt()
{
var n = (BigRational)this;
var r = n.Sqrt();
return (r.GetWholePart, r);
}
private uint[] TwosComplement(uint[] d)
{
var i = 0;
uint v = 0;
for (; i < d.Length; i++)
{
v = ~d[i] + 1;
d[i] = v;
if (v != 0)
{
i++;
break;
}
}
if (v != 0)
{
for (; i < d.Length; i++)
d[i] = ~d[i];
}
else
{
Array.Resize(ref d, d.Length + 1);
d[d.Length - 1] = 1;
}
return d;
}
public IntX Pow(int e)
{
var ans = this;
if (e == 1)
return ans;
if (e == 0)
return 1;
for (var i = 1; i != e; i++)
ans *= this;
return ans;
}
public static double Log(IntX value, double baseValue)
{
var c = 0.0;
var d = 0.5;
var dataLength = value.DataUsed;
var topBits = 0;
var x = value.Data[dataLength - 1];
while (x > 0)
{
x >>= 1;
topBits++;
}
var bitLength = (dataLength - 1) * 32 + topBits;
var bit = (uint) (1 << (topBits - 1));
for (var index = dataLength - 1; index >= 0; --index)
{
for (; bit != 0U; bit >>= 1)
{
if (((int) value.Data[index] & (int) bit) != 0)
c += d;
d *= 0.5;
}
bit = 2147483648U;
}
return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue);
}
public static List<IntX> GetFactors(IntX n)
{
var Factors = new List<IntX>();
var s = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1);
var a = Three;
while (a < s)
{
if (n % a == 0)
{
Factors.Add(a);
if (a * a != n)
Factors.Add(n / a);
}
a += 2;
}
return Factors;
}
public static IntX GreatestCommonDivisor(IntX a, IntX b)
{
while (b > 0)
{
var r = a % b;
a = b;
b = r;
}
return a;
}
public static IntX LeastCommonMultiple(IntX a, IntX b)
{
return a * b / a.Gcd(b);
}
public static double Log10(IntX value)
{
return Log(value, 10.0);
}
public static double LogN(IntX value)
{
return Log(value, 2.0);
}
public void ConstructFromArray(byte[] value, int bitLength)
{
var minSize = value.Length / DataSize;
if (value == null)
throw new ArgumentNullException("value");
DataBitWidth = bitLength;
DataLength = DataBitWidth >> 5;
var byteCount = value.Length;
var isNegative = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
var unalignedBytes = byteCount % DataSize;
var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
Data = new uint[Math.Max(dwordCount, DataLength)];
if (byteCount == 0)
return;
int curDword, curByte, byteInDword;
curByte = 3;
for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
{
byteInDword = 0;
while (byteInDword < DataSize)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
curByte--;
byteInDword++;
}
curByte += 8;
}
if (unalignedBytes != 0)
{
if (isNegative)
Data[dwordCount - 1] = 0xffffffff;
for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
{
Data[curDword] <<= 8;
Data[curDword] |= value[curByte];
}
}
}
private class IntXConverter : 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 IntX(0, DataBitWidth);
}
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);
}
}
}
public class IntXComparer : IComparer<IntX>
{
public int Compare(IntX left, IntX right)
{
return left.CompareTo(right);
}
public bool Equals(IntX left, IntX right)
{
if (left == null || right == null)
return false;
return left.Equals(right);
}
public int GetHashCode(IntX obj)
{
return obj.GetHashCode();
}
}
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] [TypeConverter(typeof(IntXConverter))] [DebuggerDisplay("{DDisplay}")] public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable { private const int DataSize = sizeof(uint); private const uint AllBits = ~(uint) 0; private const int DataSizeBits = sizeof(uint) * 8; private const uint HiNeg = (uint) 1 << (DataSizeBits - 1); private static int _dataBitWidth; private static int DataLength; public static readonly IntX One = new IntX(1, 32); public static readonly IntX Two = new IntX(2, 32); public static readonly IntX Zero = new IntX(0, 32); public static readonly IntX Ten = new IntX(10, 32); public static readonly IntX Three = new IntX(3, 32); private readonly SerializationInfo SInfo; public uint[] Data; public IntX(IntX value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; value.Data.CopyTo(Data, 0); } public IntX(string value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (!TryParse(value, out var result)) throw new Exception("TryParse Failed."); Data = new uint[DataLength]; result.Data.CopyTo(Data, 0); } public IntX(byte value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(bool value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = (uint) (value ? 1 : 0); } public IntX(char value, int bitLength) { DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(BigDecimal value, int bitLength) { var ba = value.WholePart.ToByteArray(); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var len = ba.Length / DataSize; Data = new uint[DataLength]; for (var i = 0; i < Data.Length; i++) Data[i] = BitConverter.ToUInt32(ba, i * DataSize); } public IntX(decimal value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-value, DataBitWidth); n.Data.CopyTo(Data, 0); return; } var bits = decimal.GetBits(value); Data[2] = (uint) bits[2]; Data[1] = (uint) bits[1]; Data[0] = (uint) bits[0]; } public IntX(double value, int bitLength) : this((decimal) value, bitLength) { } public IntX(float value, int bitLength) : this((decimal) value, bitLength) { } public IntX(short value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(int value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(long value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[1] = (uint) ((value >> 32) & 0xffffffff); Data[0] = (uint) (value & 0xffffffff); } public IntX(sbyte value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; if (value < 0) { var n = -new IntX(-(value + 1), DataBitWidth) - 1; n.Data.CopyTo(Data, 0); return; } Data[0] = (uint) value; } public IntX(BigRational value, int bitLength) { value.Round(1); ConstructFromArray(value.Numerator.ToByteArray(), bitLength); } public IntX(ushort value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(uint value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[0] = value; } public IntX(ulong value, int bitLength) { DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; Data[1] = (uint) ((value >> 32) & 0xffffffff); Data[0] = (uint) (value & 0xffffffff); } public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength) { } public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength) { } public IntX(byte[] value, int bitLength) { var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } public IntX(int sign, uint[] array, int bitLength) { if (array == null) throw new Exception("Array cannot be null."); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Math.Min(DataLength, array.Length); i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } if (sign < 0) Data[DataLength - 1] |= HiNeg; else Data[DataLength - 1] &= ~HiNeg; } public IntX(uint[] array, int bitLength) { if (array == null) throw new Exception("Array cannot be null."); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; if (array.Length != DataLength) Array.Resize(ref array, DataLength); Data = new uint[DataLength]; var ba = new byte[DataSize]; for (var i = 0; i < Data.Length; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } } protected IntX(SerializationInfo info, StreamingContext context) { SInfo = info; } private static int DataBitWidth { get => _dataBitWidth; set { if (value < 32) throw new Exception($"Data Bit Width {value} must not be less than 32."); _dataBitWidth = value; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DDisplay => ToString(); public IntX MaxValue { get { var r = new IntX(0, DataBitWidth); for (var i = 0; i < r.Data.Length; ++i) r.Data[i] = uint.MaxValue; r.Data[r.Data.Length - 1] = int.MaxValue; return r; } } public int BitWidth { get { IntX bw = 1; var v = new IntX(this, DataBitWidth); while ((v >>= 1) > 0) bw++; if (bw < 8) bw = 8; while (bw % 8 != 0) bw++; return (int) bw; } } public int Sign { get { var allZero = true; var ba = Data; for (var i = 0; i < ba.Length; i++) if (ba[i] != 0) { allZero = false; break; } if (allZero) return 0; return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1; } } public bool IsOne => this == 1; public bool IsEven => (this & 1) == 0; public bool IsNegative => Sign < 0; public bool IsZero { get { for (var i = 0; i < Data.Length; i++) if (Data[i] != 0) return false; return true; } } public int DataUsed { get { var DataUsed = Data.Length; if (!IsNegative) { while (DataUsed > 1 && Data[DataUsed - 1] == 0) --DataUsed; if (DataUsed == 0) DataUsed = 1; } return DataUsed; } } int IComparable.CompareTo(object obj) { return Compare(this, obj); } public int CompareTo(IntX value) { return Compare(this, value); } TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } bool IConvertible.ToBoolean(IFormatProvider provider) { return (bool) this; } byte IConvertible.ToByte(IFormatProvider provider) { return (byte) this; } char IConvertible.ToChar(IFormatProvider provider) { return (char) this; } DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return (decimal) this; } double IConvertible.ToDouble(IFormatProvider provider) { return (double) this; } short IConvertible.ToInt16(IFormatProvider provider) { return (short) this; } int IConvertible.ToInt32(IFormatProvider provider) { return (int) this; } long IConvertible.ToInt64(IFormatProvider provider) { return (long) this; } sbyte IConvertible.ToSByte(IFormatProvider provider) { return (sbyte) this; } float IConvertible.ToSingle(IFormatProvider provider) { return (float) this; } string IConvertible.ToString(IFormatProvider provider) { return ToString(null, provider); } public object ToType(Type conversionType, IFormatProvider provider) { object value; if (TryConvert(conversionType, provider, out value)) return value; throw new InvalidCastException(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { if (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt16(Data[0]); } uint IConvertible.ToUInt32(IFormatProvider provider) { if (Data[1] != 0) throw new OverflowException(); return Convert.ToUInt32(Data[0]); } ulong IConvertible.ToUInt64(IFormatProvider provider) { if (Data[1] != 0) return ((ulong) Data[1] << 32) | Data[0]; return Data[0]; } public bool Equals(IntX obj) { if (ReferenceEquals(obj, null)) return false; if (ReferenceEquals(this, obj)) return true; if (Data.Length != obj.Data.Length) return false; if (Sign != obj.Sign) return false; for (var i = 0; i < Data.Length; i++) if (Data[i] != obj.Data[i]) return false; return true; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider == null) formatProvider = CultureInfo.CurrentCulture; if (!string.IsNullOrEmpty(format)) { var ch = format[0]; if (ch == 'x' || ch == 'X') { int.TryParse(format.Substring(1).Trim(), out var min); return ToHexString(ch == 'X'); } if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd') throw new NotSupportedException("Not supported format: " + format); } return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo)), 10); } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Bits", DataBitWidth); info.AddValue("Data", Data, typeof(uint[])); } public void OnDeserialization(object sender) { if (SInfo == null) return; DataBitWidth = SInfo.GetInt32("Bits"); if (DataBitWidth != 0) { DataLength = DataBitWidth >> 5; var array = (uint[]) SInfo.GetValue("Data", typeof(uint[])); if (array == null) throw new Exception("Array cannot be null."); if (array.Length != DataLength) Array.Resize(ref array, DataLength); Data = new uint[DataLength]; var ba = new byte[4]; for (var i = 0; i < DataLength; i++) { Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize); Data[i] = BitConverter.ToUInt32(ba, 0); } } } private static byte[] ToByteArray(ulong[] value) { var ba = new byte[value.Length << 3]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3); return ba; } private static byte[] ToByteArray(uint[] value) { var ba = new byte[value.Length << 2]; Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2); return ba; } public override int GetHashCode() { var s = Sign; var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5; for (var i = 0; i < Data.Length; i++) { hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i]; hash *= 0x1000193; } return (int) hash; } public override bool Equals(object obj) { return base.Equals(obj); } public override string ToString() { return ToString(null, null); } public string ToString(string format) { return ToString(format, null); } public string ToHexString(bool caps) { var bytes = ToByteArray().Invert(); var sb = new StringBuilder(); var x = caps ? "X" : "x"; foreach (var b in bytes) { var hex = b.ToString($"{x}2"); sb.Append(hex); } return sb.ToString(); } private string ToString(NumberFormatInfo info, int radix) { if (radix < 2 || radix > 36) throw new ArgumentOutOfRangeException("radix"); if (Sign == 0) return "0"; var negative = Sign < 0; var a = new IntX(this, DataBitWidth); if (negative) try { a = -a; } catch (Exception ex) { } var biRadix = new IntX(radix, DataBitWidth); const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz"; var al = new ArrayList(); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); al.Insert(0, charSet[(int) remainder.Data[0]]); a = quotient; } catch (Exception ex) { break; } var result = new string((char[]) al.ToArray(typeof(char))); if (radix == 10 && negative) return "-" + result; return result; } public static IntX Abs(IntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.Sign < 0) return -value; return value; } 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 IntX Parse(string value) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static IntX Parse(string value, NumberStyles style) { return Parse(value, style, NumberFormatInfo.CurrentInfo); } public static IntX Parse(string value, IFormatProvider provider) { return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static IntX 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 IntX result) { return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); } public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX result) { result = 0; if (string.IsNullOrEmpty(value)) return false; if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(1); } else { if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { style |= NumberStyles.AllowHexSpecifier; value = value.Substring(2); } } if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier) return TryParseNum(value, 16, out result); return TryParseNum(value, 10, out result); } public static bool TryParseNum(string digits, int radix, out IntX result) { result = new IntX(0, DataBitWidth); if (digits == null) return false; var multiplier = new IntX(1, DataBitWidth); digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim(); var nDigits = digits[0] == '-' ? 1 : 0; for (var idx = digits.Length - 1; idx >= nDigits; idx--) { var d = (int) digits[idx]; if (d >= '0' && d <= '9') d -= '0'; else if (d >= 'A' && d <= 'Z') d = d - 'A' + 10; else return false; if (d >= radix) return false; result += multiplier * d; multiplier *= radix; if (multiplier.DataUsed == DataLength) throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} "); } if (digits[0] == '-') result = -result; return true; } public static int Compare(IntX left, object right) { if (right is IntX) return Compare(left, (IntX) right); if (right is bool) return Compare(left, new IntX((bool) right, DataBitWidth)); if (right is byte) return Compare(left, new IntX((byte) right, DataBitWidth)); if (right is char) return Compare(left, new IntX((char) right, DataBitWidth)); if (right is decimal) return Compare(left, new IntX((decimal) right, DataBitWidth)); if (right is double) return Compare(left, new IntX((double) right, DataBitWidth)); if (right is short) return Compare(left, new IntX((short) right, DataBitWidth)); if (right is int) return Compare(left, new IntX((int) right, DataBitWidth)); if (right is long) return Compare(left, new IntX((long) right, DataBitWidth)); if (right is sbyte) return Compare(left, new IntX((sbyte) right, DataBitWidth)); if (right is float) return Compare(left, new IntX((float) right, DataBitWidth)); if (right is ushort) return Compare(left, new IntX((ushort) right, DataBitWidth)); if (right is uint) return Compare(left, new IntX((uint) right, DataBitWidth)); if (right is ulong) return Compare(left, new IntX((ulong) right, DataBitWidth)); var bytes = right as byte[]; if (bytes != null) return Compare(left, new IntX(bytes, DataBitWidth)); if (right is Guid) return Compare(left, new IntX((Guid) right, DataBitWidth)); throw new ArgumentException(); } public static int Compare(IntX left, IntX right) { if (ReferenceEquals(left, right)) return 0; if (left.Sign >= 0 && right.Sign < 0) return 1; if (left.Sign < 0 && right.Sign >= 0) return -1; if (left.Data.Length != right.Data.Length) return -1; for (var i = left.Data.Length - 1; i > 0; i--) if (left.Data[i] != right.Data[i]) return left.Data[i].CompareTo(right.Data[i]); return left.Data[0].CompareTo(right.Data[0]); } public static implicit operator IntX(bool value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(byte value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(char value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(decimal value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(double value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(short value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(int value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(long value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(sbyte value) { return new IntX(value, DataBitWidth); } public static explicit operator IntX(float value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(ushort value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(uint value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(ulong value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigInteger value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigRational value) { return new IntX(value, DataBitWidth); } public static implicit operator IntX(BigDecimal value) { return new IntX(value, DataBitWidth); } public static explicit operator bool(IntX value) { return (byte) value.Data[0] != 0; } public static explicit operator byte(IntX value) { return (byte) value.Data[0]; } public static explicit operator char(IntX value) { return (char) (ushort) value.Data[0]; } public static explicit operator decimal(IntX value) { if (value.Sign == 0) return 0; if (value.Data.Length == 1) return new decimal((int) value.Data[0], 0, 0, value.Sign < 0, 0); if (value.Data.Length == 2) return new decimal((int) value.Data[0], (int) value.Data[1], 0, value.Sign < 0, 0); if (value.Data.Length == 3) return new decimal((int) value.Data[0], (int) value.Data[1], (int) value.Data[2], value.Sign < 0, 0); throw new ArgumentException("Value length exceeds decimal length."); } public static explicit operator double(IntX value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d)) throw new OverflowException(); return d; } public static explicit operator float(IntX value) { if (value.Sign == 0) return 0; var nfi = CultureInfo.InvariantCulture.NumberFormat; if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f)) throw new OverflowException(); return f; } public static explicit operator short(IntX value) { if (value.Data[0] > 0x8000) throw new OverflowException(); if (value.Data[0] == 0x8000 && value.Sign > 0) throw new OverflowException(); return (short) ((int) value.Data[0] * value.Sign); } public static explicit operator int(IntX value) { if (value.Sign == 0) return 0; return (int) value.Data[0] * value.Sign; } public static explicit operator long(IntX value) { if (value.Sign == 0) return 0; if (value.Data[0] > int.MaxValue) throw new OverflowException(); if (value.Data.Length > 1) if (value.Data[1] != 0) return (long) (((ulong) value.Data[1] << 32) | value.Data[0]) * value.Sign; return value.Data[0] * value.Sign; } public static explicit operator uint(IntX value) { if (value.Sign == 0) return 0; return value.Data[0]; } public static explicit operator ushort(IntX value) { if (value.Sign == 0) return 0; return (ushort) value.Data[0]; } public static explicit operator ulong(IntX value) { if (value.Data.Length > 1) if (value.Data[1] != 0) return ((ulong) value.Data[1] << 32) | value.Data[0]; return value.Data[0]; } public static explicit operator BigInteger(IntX value) { return new BigInteger(value.ToByteArray()); } public static explicit operator BigRational(IntX value) { return new BigRational(new BigInteger(value.ToByteArray())); } public static bool operator >(IntX left, IntX right) { return left.CompareTo(right) > 0; } public static bool operator <(IntX left, IntX right) { return Compare(left, right) < 0; } public static bool operator >=(IntX left, IntX right) { return Compare(left, right) >= 0; } public static bool operator <=(IntX left, IntX right) { return Compare(left, right) <= 0; } public static bool operator !=(IntX left, IntX right) { return !left.Equals(right); } public static bool operator ==(IntX left, IntX right) { return left.Equals(right); } public static IntX operator +(IntX value) { return value; } public static IntX operator ~(IntX value) { var da = new uint[DataLength]; for (var idx = 0; idx < DataLength; idx++) da[idx] = ~value.Data[idx]; return new IntX(da, DataBitWidth); } public static IntX operator -(IntX value) { if (ReferenceEquals(value, null)) throw new ArgumentNullException("value"); if (value.IsZero) return Zero; var da = new uint[DataLength]; for (var i = 0; i < da.Length; i++) da[i] = ~value.Data[i]; var carry = true; var index = 0; while (carry && index < da.Length) { var val = (long) da[index] + 1; da[index] = (uint) (val & AllBits); carry = val >> DataSizeBits > 0; index++; } return new IntX(da, DataBitWidth); } public static IntX operator ++(IntX value) { return value + 1; } public static IntX operator --(IntX value) { return value - 1; } public static IntX Negate(IntX value) { var ldata = (uint[]) value.Data.Clone(); for (var i = 0; i < value.Data.Length; i++) ldata[i] = ~value.Data[i]; return new IntX(value.Sign, ldata, DataBitWidth); } public static IntX operator +(IntX left, IntX right) { if (right.IsZero) return left; if (left.IsZero) return right; var dl = Math.Max(left.Data.Length, right.Data.Length); var lim = Math.Min(left.Data.Length, right.Data.Length); var result = new uint[dl]; long carry = 0; for (var i = 0; i < dl && i < lim; i++) { var sum = left.Data[i] + (long) right.Data[i] + carry; carry = sum >> 32; result[i] = (uint) (sum & 0xFFFFFFFF); } if (carry != 0) { var idx = 0; while (idx < result.Length - 1) { if (result[idx] == 0) break; idx++; } result[idx] = (uint) carry; } return new IntX(left.Sign * right.Sign, result, DataBitWidth); } public static IntX operator -(IntX left, IntX right) { if (right.IsZero) return left; if (left.IsZero) return -right; var size = Math.Max(left.Data.Length, right.Data.Length) + 1; var da = new uint[size]; long carry = 0; for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++) { var diff = left.Data[i] - (long) right.Data[i] - carry; da[i] = (uint) (diff & AllBits); carry = diff < 0 ? 1 : 0; } return new IntX(da, DataBitWidth); } public static IntX Add(IntX left, IntX right) { return left + right; } public static IntX Subtract(IntX left, IntX right) { return left - right; } public static IntX Divide(IntX dividend, IntX divisor) { if (divisor == 0) throw new DivideByZeroException(); return DivRem(dividend, divisor, out var integer); } public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quo, out var rem); remainder = new IntX(1, rem, DataBitWidth); quotient = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth); } public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder) { if (divisor == 0) throw new DivideByZeroException(); DivRem(dividend.Data, divisor.Data, out var quotient, out var rem); remainder = new IntX(1, rem, DataBitWidth); return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth); } private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder) { const ulong hiBit = 0x100000000; var divisorLen = GetLength(divisor); var dividendLen = GetLength(dividend); if (divisorLen <= 1) { ulong rem = 0; var div = divisor[0]; quotient = new uint[dividendLen]; remainder = new uint[1]; for (var i = dividendLen - 1; i >= 0; i--) { rem *= hiBit; rem += dividend[i]; var q = rem / div; rem -= q * div; quotient[i] = (uint) q; } remainder[0] = (uint) rem; return; } if (dividendLen >= divisorLen) { var shift = GetNormalizeShift(divisor[divisorLen - 1]); var normDividend = new uint[dividendLen + 1]; var normDivisor = new uint[divisorLen]; Normalize(dividend, dividendLen, normDividend, shift); Normalize(divisor, divisorLen, normDivisor, shift); quotient = new uint[dividendLen - divisorLen + 1]; for (var j = dividendLen - divisorLen; j >= 0; j--) { var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1]; var qj = dx / normDivisor[divisorLen - 1]; dx -= qj * normDivisor[divisorLen - 1]; do { if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2]) break; qj -= 1L; dx += normDivisor[divisorLen - 1]; } while (dx < hiBit); ulong di = 0; ulong dj; var index = 0; while (index < divisorLen) { var dqj = normDivisor[index] * qj; dj = normDividend[index + j] - (uint) dqj - di; normDividend[index + j] = (uint) dj; dqj = dqj >> 32; dj = dj >> 32; di = dqj - dj; index++; } dj = normDividend[j + divisorLen] - di; normDividend[j + divisorLen] = (uint) dj; quotient[j] = (uint) qj; if ((long) dj < 0) { quotient[j]--; ulong sum = 0; for (index = 0; index < divisorLen; index++) { sum = normDivisor[index] + normDividend[j + index] + sum; normDividend[j + index] = (uint) sum; sum = sum >> 32; } sum += normDividend[j + divisorLen]; normDividend[j + divisorLen] = (uint) sum; } } remainder = Unnormalize(normDividend, shift); return; } quotient = new uint[1]; remainder = dividend; } private static int GetLength(uint[] uints) { var index = uints.Length - 1; while (index >= 0 && uints[index] == 0) index--; return index + 1; } private static int GetNormalizeShift(uint ui) { var shift = 0; if ((ui & 0xffff0000) == 0) { ui = ui << 16; shift += 16; } if ((ui & 0xff000000) == 0) { ui = ui << 8; shift += 8; } if ((ui & 0xf0000000) == 0) { ui = ui << 4; shift += 4; } if ((ui & 0xc0000000) == 0) { ui = ui << 2; shift += 2; } if ((ui & 0x80000000) == 0) shift++; return shift; } private static uint[] Unnormalize(uint[] normalized, int shift) { var len = GetLength(normalized); var unnormalized = new uint[len]; if (shift > 0) { var rshift = 32 - shift; uint r = 0; for (var i = len - 1; i >= 0; i--) { unnormalized[i] = (normalized[i] >> shift) | r; r = normalized[i] << rshift; } } else { for (var j = 0; j < len; j++) unnormalized[j] = normalized[j]; } return unnormalized; } private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift) { int i; uint n = 0; if (shift > 0) { var rShift = 32 - shift; for (i = 0; i < len; i++) { normalized[i] = (unormalized[i] << shift) | n; n = unormalized[i] >> rShift; } } else { i = 0; while (i < len) { normalized[i] = unormalized[i]; i++; } } while (i < normalized.Length) normalized[i++] = 0; if (n != 0) normalized[len] = n; } public static IntX Remainder(IntX dividend, IntX divisor) { DivRem(dividend, divisor, out var remainder); return remainder; } public static IntX Max(IntX left, IntX right) { return left.CompareTo(right) < 0 ? right : left; } public static IntX Min(IntX left, IntX right) { return left.CompareTo(right) <= 0 ? left : right; } public static IntX operator %(IntX dividend, IntX divisor) { return Remainder(dividend, divisor); } public static IntX operator /(IntX dividend, IntX divisor) { return Divide(dividend, divisor); } public ulong[] ToUIn64Array() { var al = Data.Length >> 1; if (al * 2 != Data.Length) al++; var arr = new ulong[al]; Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2); return arr; } public uint[] ToUIn32Array() { return Data; } public byte[] ToByteArray() { var ba = new byte[Data.Length * DataSize]; Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize); return ba; } private void TrimToMsb() { var dataUsed = Data.Length; while (dataUsed > 1 && Data[dataUsed - 1] == 0) --dataUsed; if (dataUsed != Data.Length) { var tData = new uint[dataUsed]; for (var i = 0; i < dataUsed; i++) tData[i] = Data[i]; Data = (uint[]) tData.Clone(); } } public static IntX Multiply(IntX left, IntX right) { if (left == 0 || right == 0) return Zero; if (left == 1 && right != 1) return right; if (left != 1 && right == 1) return left; if (left == 1 && right == 1) return One; var xInts = left.Data; var yInts = right.Data; var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1]; for (var i = 0; i < xInts.Length; i++) { var index = i; ulong remainder = 0; foreach (var yi in yInts) { remainder = remainder + (ulong) xInts[i] * yi + mulInts[index]; mulInts[index++] = (uint) remainder; remainder = remainder >> 32; } while (remainder != 0) { remainder += mulInts[index]; mulInts[index++] = (uint) remainder; remainder = remainder >> 32; } } return new IntX(left.Sign * right.Sign, mulInts, DataBitWidth); } public static IntX operator *(IntX left, IntX right) { return Multiply(left, right); } public static IntX operator >>(IntX value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value << int.MaxValue << 1; if (shift < 0) return value << -shift; var xd = value.Data; var shiftAmount = 32; var invShift = 0; var bufLen = xd.Length; while (bufLen > 1 && xd[bufLen - 1] == 0) bufLen--; for (var count = shift; count > 0; count -= shiftAmount) { if (count < shiftAmount) { shiftAmount = count; invShift = 32 - shiftAmount; } ulong carry = 0; for (var i = bufLen - 1; i >= 0; i--) { var val = (ulong) xd[i] >> shiftAmount; val |= carry; carry = (ulong) xd[i] << invShift; xd[i] = (uint) val; } } return new IntX(value.Sign, xd, DataBitWidth); } public static IntX operator <<(IntX value, int shift) { if (shift == 0) return value; if (shift == int.MinValue) return value >> int.MaxValue >> 1; if (shift < 0) return value >> -shift; var digitShift = shift / 32; var smallShift = shift - digitShift * 32; var xd = value.Data; var xl = xd.Length; var zd = new uint[xl + digitShift + 1]; if (smallShift == 0) { for (var index = 0; index < xl; ++index) zd[index + digitShift] = xd[index]; } else { var carryShift = 32 - smallShift; uint carry = 0; int index; for (index = 0; index < xl; ++index) { var rot = xd[index]; zd[index + digitShift] = (rot << smallShift) | carry; carry = rot >> carryShift; } zd[index + digitShift] = carry; } return new IntX(value.Sign, zd, DataBitWidth); } public static IntX operator |(IntX left, IntX right) { if (left == 0) return right; if (right == 0) return left; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu | yu; } return new IntX(left.Sign * right.Sign, z, DataBitWidth); } public static IntX operator ^(IntX left, IntX right) { var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu ^ yu; } return new IntX(left.Sign * right.Sign, z, DataBitWidth); } public static IntX operator &(IntX left, IntX right) { if (left == 0 || right == 0) return 0; var z = new uint[Math.Max(left.Data.Length, right.Data.Length)]; var lExt = left.Sign < 0 ? uint.MaxValue : 0U; var rExt = right.Sign < 0 ? uint.MaxValue : 0U; for (var i = 0; i < z.Length; i++) { var xu = i < left.Data.Length ? left.Data[i] : lExt; var yu = i < right.Data.Length ? right.Data[i] : rExt; z[i] = xu & yu; } return new IntX(left.Sign * right.Sign, z, DataBitWidth); } public string ToBinaryString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base2 = new StringBuilder(bytes.Length * 8); var binary = Convert.ToString(bytes[index], 2); if (binary[0] != '0' && Sign == 1) base2.Append('0'); base2.Append(binary); for (index--; index >= 0; index--) base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0')); return base2.ToString(); } public string ToOctalString() { var bytes = ToByteArray(); var index = bytes.Length - 1; var base8 = new StringBuilder((bytes.Length / 3 + 1) * 8); var rem = bytes.Length % 3; if (rem == 0) rem = 3; var base0 = 0; while (rem != 0) { base0 <<= 8; base0 += bytes[index--]; rem--; } var octal = Convert.ToString(base0, 8); if (octal[0] != '0' && Sign == 1) base8.Append('0'); base8.Append(octal); while (index >= 0) { base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2]; base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0')); index -= 3; } return base8.ToString(); } public static IntX Pow(IntX value, IntX exponent, int bitLength) { if (value == null) throw new ArgumentNullException("Value cannot be null"); if (exponent == null) throw new ArgumentNullException("Exponent cannot be null"); if (exponent < 0) throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative"); var result = new IntX("1", bitLength); while (exponent != 0) { if ((exponent & 1) != 0) result *= value; exponent >>= 1; value *= value; } return result; } public static IntX ModPow(IntX n, IntX e, IntX m) { var n1 = new IntX(n, DataBitWidth); var e1 = new IntX(e, DataBitWidth); var r = new IntX(1, DataBitWidth); while (e1 != 0) { if (e1 % 2 == 1) r = r * n1 % m; e1 >>= 1; n1 = n1 * n1 % m; } return r; } public static int GetSign(uint[] value) { var allZero = true; for (var i = 0; i < value.Length; i++) if (value[i] != 0) { allZero = false; break; } if (allZero) return 0; return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1; } private static int GetDataUsed(uint[] array) { var neg = GetSign(array) < 0; var dataUsed = array.Length; if (!neg) { while (dataUsed > 1 && array[dataUsed - 1] == 0) --dataUsed; if (dataUsed == 0) dataUsed = 1; } return dataUsed; } public int GetDecimalPlaces() { var dPlaces = 0; if (Sign == 0) return 1; var a = new IntX(this, DataBitWidth); if (Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new IntX(10, DataBitWidth); while (a > 0) try { Divide(a, biRadix, out var remainder, out var quotient); a = quotient; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } public (IntX approximateRoot, BigRational realRoot) Sqrt() { var n = (BigRational)this; var r = n.Sqrt(); return (r.GetWholePart, r); } private uint[] TwosComplement(uint[] d) { var i = 0; uint v = 0; for (; i < d.Length; i++) { v = ~d[i] + 1; d[i] = v; if (v != 0) { i++; break; } } if (v != 0) { for (; i < d.Length; i++) d[i] = ~d[i]; } else { Array.Resize(ref d, d.Length + 1); d[d.Length - 1] = 1; } return d; } public IntX Pow(int e) { var ans = this; if (e == 1) return ans; if (e == 0) return 1; for (var i = 1; i != e; i++) ans *= this; return ans; } public static double Log(IntX value, double baseValue) { var c = 0.0; var d = 0.5; var dataLength = value.DataUsed; var topBits = 0; var x = value.Data[dataLength - 1]; while (x > 0) { x >>= 1; topBits++; } var bitLength = (dataLength - 1) * 32 + topBits; var bit = (uint) (1 << (topBits - 1)); for (var index = dataLength - 1; index >= 0; --index) { for (; bit != 0U; bit >>= 1) { if (((int) value.Data[index] & (int) bit) != 0) c += d; d *= 0.5; } bit = 2147483648U; } return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue); } public static List<IntX> GetFactors(IntX n) { var Factors = new List<IntX>(); var s = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1); var a = Three; while (a < s) { if (n % a == 0) { Factors.Add(a); if (a * a != n) Factors.Add(n / a); } a += 2; } return Factors; } public static IntX GreatestCommonDivisor(IntX a, IntX b) { while (b > 0) { var r = a % b; a = b; b = r; } return a; } public static IntX LeastCommonMultiple(IntX a, IntX b) { return a * b / a.Gcd(b); } public static double Log10(IntX value) { return Log(value, 10.0); } public static double LogN(IntX value) { return Log(value, 2.0); } public void ConstructFromArray(byte[] value, int bitLength) { var minSize = value.Length / DataSize; if (value == null) throw new ArgumentNullException("value"); DataBitWidth = bitLength; DataLength = DataBitWidth >> 5; var byteCount = value.Length; var isNegative = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80; var unalignedBytes = byteCount % DataSize; var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1); Data = new uint[Math.Max(dwordCount, DataLength)]; if (byteCount == 0) return; int curDword, curByte, byteInDword; curByte = 3; for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++) { byteInDword = 0; while (byteInDword < DataSize) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; curByte--; byteInDword++; } curByte += 8; } if (unalignedBytes != 0) { if (isNegative) Data[dwordCount - 1] = 0xffffffff; for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--) { Data[curDword] <<= 8; Data[curDword] |= value[curByte]; } } } private class IntXConverter : 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 IntX(0, DataBitWidth); } 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); } } } public class IntXComparer : IComparer<IntX> { public int Compare(IntX left, IntX right) { return left.CompareTo(right); } public bool Equals(IntX left, IntX right) { if (left == null || right == null) return false; return left.Equals(right); } public int GetHashCode(IntX obj) { return obj.GetHashCode(); } }
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(IntXConverter))]
[DebuggerDisplay("{DDisplay}")]
public class IntX : IComparable<IntX>, IComparable, IEquatable<IntX>, IConvertible, IFormattable, ISerializable
{
    private const          int               DataSize     = sizeof(uint);
    private const          uint              AllBits      = ~(uint) 0;
    private const          int               DataSizeBits = sizeof(uint) * 8;
    private const          uint              HiNeg        = (uint) 1 << (DataSizeBits - 1);
    private static         int               _dataBitWidth;
    private static         int               DataLength;
    public static readonly IntX              One   = new IntX(1,  32);
    public static readonly IntX              Two   = new IntX(2,  32);
    public static readonly IntX              Zero  = new IntX(0,  32);
    public static readonly IntX              Ten   = new IntX(10, 32);
    public static readonly IntX              Three = new IntX(3,  32);
    private readonly       SerializationInfo SInfo;
    public                 uint[]            Data;
    public IntX(IntX value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        value.Data.CopyTo(Data, 0);
    }
    public IntX(string value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        if (!TryParse(value, out var result))
            throw new Exception("TryParse Failed.");
        Data = new uint[DataLength];
        result.Data.CopyTo(Data, 0);
    }
    public IntX(byte value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(bool value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = (uint) (value ? 1 : 0);
    }
    public IntX(char value, int bitLength)
    {
        DataLength = DataBitWidth >> 5;
        Data       = new uint[DataLength];
        Data[0]    = value;
    }
    public IntX(BigDecimal value, int bitLength)
    {
        var ba = value.WholePart.ToByteArray();
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        var len = ba.Length / DataSize;
        Data = new uint[DataLength];
        for (var i = 0; i < Data.Length; i++)
            Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
    }
    public IntX(decimal value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-value, DataBitWidth);
            n.Data.CopyTo(Data, 0);
            return;
        }
        var bits = decimal.GetBits(value);
        Data[2] = (uint) bits[2];
        Data[1] = (uint) bits[1];
        Data[0] = (uint) bits[0];
    }
    public IntX(double value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public IntX(float value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public IntX(short value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(int value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(long value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[1] = (uint) ((value >> 32) & 0xffffffff);
        Data[0] = (uint) (value         & 0xffffffff);
    }
    public IntX(sbyte value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new IntX(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public IntX(BigRational value, int bitLength)
    {
        value.Round(1);
        ConstructFromArray(value.Numerator.ToByteArray(), bitLength);
    }
    public IntX(ushort value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(uint value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public IntX(ulong value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        Data[1]      = (uint) ((value >> 32) & 0xffffffff);
        Data[0]      = (uint) (value         & 0xffffffff);
    }
    public IntX(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public IntX(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public IntX(byte[] value, int bitLength)
    {
        var minSize = value.Length / DataSize;
        if (value == null)
            throw new ArgumentNullException("value");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        var byteCount      = value.Length;
        var isNegative     = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
            }
        }
    }
    public IntX(int sign, uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        Data         = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Math.Min(DataLength, array.Length); i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
        if (sign < 0)
            Data[DataLength - 1] |= HiNeg;
        else
            Data[DataLength - 1] &= ~HiNeg;
    }
    public IntX(uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> 5;
        if (array.Length != DataLength)
            Array.Resize(ref array, DataLength);
        Data = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Data.Length; i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
    }
    protected IntX(SerializationInfo info, StreamingContext context)
    {
        SInfo = info;
    }
    private static int DataBitWidth
    {
        get => _dataBitWidth;
        set
        {
            if (value < 32)
                throw new Exception($"Data Bit Width {value} must not be less than 32.");
            _dataBitWidth = value;
        }
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => ToString();
    public IntX MaxValue
    {
        get
        {
            var r = new IntX(0, DataBitWidth);
            for (var i = 0; i < r.Data.Length; ++i)
                r.Data[i] = uint.MaxValue;
            r.Data[r.Data.Length - 1] = int.MaxValue;
            return r;
        }
    }
    public int BitWidth
    {
        get
        {
            IntX bw = 1;
            var  v  = new IntX(this, DataBitWidth);
            while ((v >>= 1) > 0)
                bw++;
            if (bw < 8)
                bw = 8;
            while (bw % 8 != 0)
                bw++;
            return (int) bw;
        }
    }
    public int Sign
    {
        get
        {
            var allZero = true;
            var ba      = Data;
            for (var i = 0; i < ba.Length; i++)
                if (ba[i] != 0)
                {
                    allZero = false;
                    break;
                }
            if (allZero)
                return 0;
            return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1;
        }
    }
    public bool IsOne      => this       == 1;
    public bool IsEven     => (this & 1) == 0;
    public bool IsNegative => Sign       < 0;
    public bool IsZero
    {
        get
        {
            for (var i = 0; i < Data.Length; i++)
                if (Data[i] != 0)
                    return false;
            return true;
        }
    }
    public int DataUsed
    {
        get
        {
            var DataUsed = Data.Length;
            if (!IsNegative)
            {
                while (DataUsed > 1 && Data[DataUsed - 1] == 0)
                    --DataUsed;
                if (DataUsed == 0)
                    DataUsed = 1;
            }
            return DataUsed;
        }
    }
    int IComparable.CompareTo(object obj)
    {
        return Compare(this, obj);
    }
    public int CompareTo(IntX value)
    {
        return Compare(this, value);
    }
    TypeCode IConvertible.GetTypeCode()
    {
        return TypeCode.Object;
    }
    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        return (bool) this;
    }
    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return (byte) this;
    }
    char IConvertible.ToChar(IFormatProvider provider)
    {
        return (char) this;
    }
    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }
    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        return (decimal) this;
    }
    double IConvertible.ToDouble(IFormatProvider provider)
    {
        return (double) this;
    }
    short IConvertible.ToInt16(IFormatProvider provider)
    {
        return (short) this;
    }
    int IConvertible.ToInt32(IFormatProvider provider)
    {
        return (int) this;
    }
    long IConvertible.ToInt64(IFormatProvider provider)
    {
        return (long) this;
    }
    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        return (sbyte) this;
    }
    float IConvertible.ToSingle(IFormatProvider provider)
    {
        return (float) this;
    }
    string IConvertible.ToString(IFormatProvider provider)
    {
        return ToString(null, provider);
    }
    public object ToType(Type conversionType, IFormatProvider provider)
    {
        object value;
        if (TryConvert(conversionType, provider, out value))
            return value;
        throw new InvalidCastException();
    }
    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt16(Data[0]);
    }
    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt32(Data[0]);
    }
    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        if (Data[1] != 0)
            return ((ulong) Data[1] << 32) | Data[0];
        return Data[0];
    }
    public bool Equals(IntX obj)
    {
        if (ReferenceEquals(obj, null))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (Data.Length != obj.Data.Length)
            return false;
        if (Sign != obj.Sign)
            return false;
        for (var i = 0; i < Data.Length; i++)
            if (Data[i] != obj.Data[i])
                return false;
        return true;
    }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (formatProvider == null)
            formatProvider = CultureInfo.CurrentCulture;
        if (!string.IsNullOrEmpty(format))
        {
            var ch = format[0];
            if (ch == 'x' || ch == 'X')
            {
                int.TryParse(format.Substring(1).Trim(), out var min);
                return ToHexString(ch == 'X');
            }
            if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
                throw new NotSupportedException("Not supported format: " + format);
        }
        return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bits", DataBitWidth);
        info.AddValue("Data", Data, typeof(uint[]));
    }
    public void OnDeserialization(object sender)
    {
        if (SInfo == null)
            return;
        DataBitWidth = SInfo.GetInt32("Bits");
        if (DataBitWidth != 0)
        {
            DataLength = DataBitWidth >> 5;
            var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
            if (array == null)
                throw new Exception("Array cannot be null.");
            if (array.Length != DataLength)
                Array.Resize(ref array, DataLength);
            Data = new uint[DataLength];
            var ba = new byte[4];
            for (var i = 0; i < DataLength; i++)
            {
                Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
                Data[i] = BitConverter.ToUInt32(ba, 0);
            }
        }
    }
    private static byte[] ToByteArray(ulong[] value)
    {
        var ba = new byte[value.Length << 3];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
        return ba;
    }
    private static byte[] ToByteArray(uint[] value)
    {
        var ba = new byte[value.Length << 2];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
        return ba;
    }
    public override int GetHashCode()
    {
        var s    = Sign;
        var hash = ((s << 7) | (s >> 25)) ^ 0x811c9dc5;
        for (var i = 0; i < Data.Length; i++)
        {
            hash ^= ((hash << 7) | (hash >> 25)) ^ Data[i];
            hash *= 0x1000193;
        }
        return (int) hash;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override string ToString()
    {
        return ToString(null, null);
    }
    public string ToString(string format)
    {
        return ToString(format, null);
    }
    public string ToHexString(bool caps)
    {
        var bytes = ToByteArray().Invert();
        var sb    = new StringBuilder();
        var x     = caps ? "X" : "x";
        foreach (var b in bytes)
        {
            var hex = b.ToString($"{x}2");
            sb.Append(hex);
        }
        return sb.ToString();
    }
    private string ToString(NumberFormatInfo info, int radix)
    {
        if (radix < 2 || radix > 36)
            throw new ArgumentOutOfRangeException("radix");
        if (Sign == 0)
            return "0";
        var negative = Sign < 0;
        var a        = new IntX(this, DataBitWidth);
        if (negative)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
            }
        var          biRadix = new IntX(radix, DataBitWidth);
        const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
        var          al      = new ArrayList();
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                al.Insert(0, charSet[(int) remainder.Data[0]]);
                a = quotient;
            }
            catch (Exception ex)
            {
                break;
            }
        var result = new string((char[]) al.ToArray(typeof(char)));
        if (radix == 10 && negative)
            return "-" + result;
        return result;
    }
    public static IntX Abs(IntX value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.Sign < 0)
            return -value;
        return value;
    }
    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 IntX Parse(string value)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
    }
    public static IntX Parse(string value, NumberStyles style)
    {
        return Parse(value, style, NumberFormatInfo.CurrentInfo);
    }
    public static IntX Parse(string value, IFormatProvider provider)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
    }
    public static IntX 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 IntX result)
    {
        return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
    }
    public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out IntX result)
    {
        result = 0;
        if (string.IsNullOrEmpty(value))
            return false;
        if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
        {
            style |= NumberStyles.AllowHexSpecifier;
            value =  value.Substring(1);
        }
        else
        {
            if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
            {
                style |= NumberStyles.AllowHexSpecifier;
                value =  value.Substring(2);
            }
        }
        if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
            return TryParseNum(value, 16, out result);
        return TryParseNum(value, 10, out result);
    }
    public static bool TryParseNum(string digits, int radix, out IntX result)
    {
        result = new IntX(0, DataBitWidth);
        if (digits == null)
            return false;
        var multiplier = new IntX(1, DataBitWidth);
        digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
        var nDigits = digits[0] == '-' ? 1 : 0;
        for (var idx = digits.Length - 1; idx >= nDigits; idx--)
        {
            var d = (int) digits[idx];
            if (d >= '0' && d <= '9')
                d -= '0';
            else if (d >= 'A' && d <= 'Z')
                d = d - 'A' + 10;
            else
                return false;
            if (d >= radix)
                return false;
            result     += multiplier * d;
            multiplier *= radix;
            if (multiplier.DataUsed == DataLength)
                throw new Exception($"Data overflow in Multiplier {new StackFrame(1, true).GetFileLineNumber()} ");
        }
        if (digits[0] == '-')
            result = -result;
        return true;
    }
    public static int Compare(IntX left, object right)
    {
        if (right is IntX)
            return Compare(left, (IntX) right);
        if (right is bool)
            return Compare(left, new IntX((bool) right, DataBitWidth));
        if (right is byte)
            return Compare(left, new IntX((byte) right, DataBitWidth));
        if (right is char)
            return Compare(left, new IntX((char) right, DataBitWidth));
        if (right is decimal)
            return Compare(left, new IntX((decimal) right, DataBitWidth));
        if (right is double)
            return Compare(left, new IntX((double) right, DataBitWidth));
        if (right is short)
            return Compare(left, new IntX((short) right, DataBitWidth));
        if (right is int)
            return Compare(left, new IntX((int) right, DataBitWidth));
        if (right is long)
            return Compare(left, new IntX((long) right, DataBitWidth));
        if (right is sbyte)
            return Compare(left, new IntX((sbyte) right, DataBitWidth));
        if (right is float)
            return Compare(left, new IntX((float) right, DataBitWidth));
        if (right is ushort)
            return Compare(left, new IntX((ushort) right, DataBitWidth));
        if (right is uint)
            return Compare(left, new IntX((uint) right, DataBitWidth));
        if (right is ulong)
            return Compare(left, new IntX((ulong) right, DataBitWidth));
        var bytes = right as byte[];
        if (bytes != null)
            return Compare(left, new IntX(bytes, DataBitWidth));
        if (right is Guid)
            return Compare(left, new IntX((Guid) right, DataBitWidth));
        throw new ArgumentException();
    }
    public static int Compare(IntX left, IntX right)
    {
        if (ReferenceEquals(left, right))
            return 0;
        if (left.Sign >= 0 && right.Sign < 0)
            return 1;
        if (left.Sign < 0 && right.Sign >= 0)
            return -1;
        if (left.Data.Length != right.Data.Length)
            return -1;
        for (var i = left.Data.Length - 1; i > 0; i--)
            if (left.Data[i] != right.Data[i])
                return left.Data[i].CompareTo(right.Data[i]);
        return left.Data[0].CompareTo(right.Data[0]);
    }
    public static implicit operator IntX(bool value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(byte value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(char value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(decimal value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(double value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(short value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(int value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(long value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(sbyte value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator IntX(float value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(ushort value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(uint value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(ulong value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigInteger value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigRational value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static implicit operator IntX(BigDecimal value)
    {
        return new IntX(value, DataBitWidth);
    }
    public static explicit operator bool(IntX value)
    {
        return (byte) value.Data[0] != 0;
    }
    public static explicit operator byte(IntX value)
    {
        return (byte) value.Data[0];
    }
    public static explicit operator char(IntX value)
    {
        return (char) (ushort) value.Data[0];
    }
    public static explicit operator decimal(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data.Length == 1)
            return new decimal((int) value.Data[0], 0, 0, value.Sign < 0, 0);
        if (value.Data.Length == 2)
            return new decimal((int) value.Data[0], (int) value.Data[1], 0, value.Sign < 0, 0);
        if (value.Data.Length == 3)
            return new decimal((int) value.Data[0], (int) value.Data[1], (int) value.Data[2], value.Sign < 0, 0);
        throw new ArgumentException("Value length exceeds decimal length.");
    }
    public static explicit operator double(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
            throw new OverflowException();
        return d;
    }
    public static explicit operator float(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
            throw new OverflowException();
        return f;
    }
    public static explicit operator short(IntX value)
    {
        if (value.Data[0] > 0x8000)
            throw new OverflowException();
        if (value.Data[0] == 0x8000 && value.Sign > 0)
            throw new OverflowException();
        return (short) ((int) value.Data[0] * value.Sign);
    }
    public static explicit operator int(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (int) value.Data[0] * value.Sign;
    }
    public static explicit operator long(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data[0] > int.MaxValue)
            throw new OverflowException();
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return (long) (((ulong) value.Data[1] << 32) | value.Data[0]) * value.Sign;
        return value.Data[0] * value.Sign;
    }
    public static explicit operator uint(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return value.Data[0];
    }
    public static explicit operator ushort(IntX value)
    {
        if (value.Sign == 0)
            return 0;
        return (ushort) value.Data[0];
    }
    public static explicit operator ulong(IntX value)
    {
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return ((ulong) value.Data[1] << 32) | value.Data[0];
        return value.Data[0];
    }
    public static explicit operator BigInteger(IntX value)
    {
        return new BigInteger(value.ToByteArray());
    }
    public static explicit operator BigRational(IntX value)
    {
        return new BigRational(new BigInteger(value.ToByteArray()));
    }
    public static bool operator >(IntX left, IntX right)
    {
        return left.CompareTo(right) > 0;
    }
    public static bool operator <(IntX left, IntX right)
    {
        return Compare(left, right) < 0;
    }
    public static bool operator >=(IntX left, IntX right)
    {
        return Compare(left, right) >= 0;
    }
    public static bool operator <=(IntX left, IntX right)
    {
        return Compare(left, right) <= 0;
    }
    public static bool operator !=(IntX left, IntX right)
    {
        return !left.Equals(right);
    }
    public static bool operator ==(IntX left, IntX right)
    {
        return left.Equals(right);
    }
    public static IntX operator +(IntX value)
    {
        return value;
    }
    public static IntX operator ~(IntX value)
    {
        var da = new uint[DataLength];
        for (var idx = 0; idx < DataLength; idx++)
            da[idx] = ~value.Data[idx];
        return new IntX(da, DataBitWidth);
    }
    public static IntX operator -(IntX value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.IsZero)
            return Zero;
        var da = new uint[DataLength];
        for (var i = 0; i < da.Length; i++)
            da[i] = ~value.Data[i];
        var carry = true;
        var index = 0;
        while (carry && index < da.Length)
        {
            var val = (long) da[index] + 1;
            da[index] = (uint) (val & AllBits);
            carry     = val >> DataSizeBits > 0;
            index++;
        }
        return new IntX(da, DataBitWidth);
    }
    public static IntX operator ++(IntX value)
    {
        return value + 1;
    }
    public static IntX operator --(IntX value)
    {
        return value - 1;
    }
    public static IntX Negate(IntX value)
    {
        var ldata = (uint[]) value.Data.Clone();
        for (var i = 0; i < value.Data.Length; i++)
            ldata[i] = ~value.Data[i];
        return new IntX(value.Sign, ldata, DataBitWidth);
    }
    public static IntX operator +(IntX left, IntX right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return right;
        var  dl     = Math.Max(left.Data.Length, right.Data.Length);
        var  lim    = Math.Min(left.Data.Length, right.Data.Length);
        var  result = new uint[dl];
        long carry  = 0;
        for (var i = 0; i < dl && i < lim; i++)
        {
            var sum = left.Data[i] + (long) right.Data[i] + carry;
            carry     = sum >> 32;
            result[i] = (uint) (sum & 0xFFFFFFFF);
        }
        if (carry != 0)
        {
            var idx = 0;
            while (idx < result.Length - 1)
            {
                if (result[idx] == 0)
                    break;
                idx++;
            }
            result[idx] = (uint) carry;
        }
        return new IntX(left.Sign * right.Sign, result, DataBitWidth);
    }
    public static IntX operator -(IntX left, IntX right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return -right;
        var  size  = Math.Max(left.Data.Length, right.Data.Length) + 1;
        var  da    = new uint[size];
        long carry = 0;
        for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++)
        {
            var diff = left.Data[i] - (long) right.Data[i] - carry;
            da[i] = (uint) (diff & AllBits);
            carry = diff < 0 ? 1 : 0;
        }
        return new IntX(da, DataBitWidth);
    }
    public static IntX Add(IntX left, IntX right)
    {
        return left + right;
    }
    public static IntX Subtract(IntX left, IntX right)
    {
        return left - right;
    }
    public static IntX Divide(IntX dividend, IntX divisor)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        return DivRem(dividend, divisor, out var integer);
    }
    public static void Divide(IntX dividend, IntX divisor, out IntX remainder, out IntX quotient)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
        remainder = new IntX(1,                            rem, DataBitWidth);
        quotient  = new IntX(dividend.Sign * divisor.Sign, quo, DataBitWidth);
    }
    public static IntX DivRem(IntX dividend, IntX divisor, out IntX remainder)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
        remainder = new IntX(1, rem, DataBitWidth);
        return new IntX(dividend.Sign * divisor.Sign, quotient, DataBitWidth);
    }
    private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
    {
        const ulong hiBit       = 0x100000000;
        var         divisorLen  = GetLength(divisor);
        var         dividendLen = GetLength(dividend);
        if (divisorLen <= 1)
        {
            ulong rem = 0;
            var   div = divisor[0];
            quotient  = new uint[dividendLen];
            remainder = new uint[1];
            for (var i = dividendLen - 1; i >= 0; i--)
            {
                rem *= hiBit;
                rem += dividend[i];
                var q = rem / div;
                rem         -= q * div;
                quotient[i] =  (uint) q;
            }
            remainder[0] = (uint) rem;
            return;
        }
        if (dividendLen >= divisorLen)
        {
            var shift        = GetNormalizeShift(divisor[divisorLen - 1]);
            var normDividend = new uint[dividendLen + 1];
            var normDivisor  = new uint[divisorLen];
            Normalize(dividend, dividendLen, normDividend, shift);
            Normalize(divisor,  divisorLen,  normDivisor,  shift);
            quotient = new uint[dividendLen - divisorLen + 1];
            for (var j = dividendLen - divisorLen; j >= 0; j--)
            {
                var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
                var qj = dx / normDivisor[divisorLen                                        - 1];
                dx -= qj * normDivisor[divisorLen - 1];
                do
                {
                    if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
                        break;
                    qj -= 1L;
                    dx += normDivisor[divisorLen - 1];
                } while (dx < hiBit);
                ulong di = 0;
                ulong dj;
                var   index = 0;
                while (index < divisorLen)
                {
                    var dqj = normDivisor[index] * qj;
                    dj                      = normDividend[index + j] - (uint) dqj - di;
                    normDividend[index + j] = (uint) dj;
                    dqj                     = dqj >> 32;
                    dj                      = dj  >> 32;
                    di                      = dqj - dj;
                    index++;
                }
                dj                           = normDividend[j + divisorLen] - di;
                normDividend[j + divisorLen] = (uint) dj;
                quotient[j]                  = (uint) qj;
                if ((long) dj < 0)
                {
                    quotient[j]--;
                    ulong sum = 0;
                    for (index = 0; index < divisorLen; index++)
                    {
                        sum                     = normDivisor[index] + normDividend[j + index] + sum;
                        normDividend[j + index] = (uint) sum;
                        sum                     = sum >> 32;
                    }
                    sum += normDividend[j + divisorLen];
                    normDividend[j        + divisorLen] = (uint) sum;
                }
            }
            remainder = Unnormalize(normDividend, shift);
            return;
        }
        quotient  = new uint[1];
        remainder = dividend;
    }
    private static int GetLength(uint[] uints)
    {
        var index = uints.Length - 1;
        while (index >= 0 && uints[index] == 0)
            index--;
        return index + 1;
    }
    private static int GetNormalizeShift(uint ui)
    {
        var shift = 0;
        if ((ui & 0xffff0000) == 0)
        {
            ui    =  ui << 16;
            shift += 16;
        }
        if ((ui & 0xff000000) == 0)
        {
            ui    =  ui << 8;
            shift += 8;
        }
        if ((ui & 0xf0000000) == 0)
        {
            ui    =  ui << 4;
            shift += 4;
        }
        if ((ui & 0xc0000000) == 0)
        {
            ui    =  ui << 2;
            shift += 2;
        }
        if ((ui & 0x80000000) == 0)
            shift++;
        return shift;
    }
    private static uint[] Unnormalize(uint[] normalized, int shift)
    {
        var len          = GetLength(normalized);
        var unnormalized = new uint[len];
        if (shift > 0)
        {
            var  rshift = 32 - shift;
            uint r      = 0;
            for (var i = len - 1; i >= 0; i--)
            {
                unnormalized[i] = (normalized[i] >> shift) | r;
                r               = normalized[i] << rshift;
            }
        }
        else
        {
            for (var j = 0; j < len; j++)
                unnormalized[j] = normalized[j];
        }
        return unnormalized;
    }
    private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
    {
        int  i;
        uint n = 0;
        if (shift > 0)
        {
            var rShift = 32 - shift;
            for (i = 0; i < len; i++)
            {
                normalized[i] = (unormalized[i] << shift) | n;
                n             = unormalized[i] >> rShift;
            }
        }
        else
        {
            i = 0;
            while (i < len)
            {
                normalized[i] = unormalized[i];
                i++;
            }
        }
        while (i < normalized.Length)
            normalized[i++] = 0;
        if (n != 0)
            normalized[len] = n;
    }
    public static IntX Remainder(IntX dividend, IntX divisor)
    {
        DivRem(dividend, divisor, out var remainder);
        return remainder;
    }
    public static IntX Max(IntX left, IntX right)
    {
        return left.CompareTo(right) < 0 ? right : left;
    }
    public static IntX Min(IntX left, IntX right)
    {
        return left.CompareTo(right) <= 0 ? left : right;
    }
    public static IntX operator %(IntX dividend, IntX divisor)
    {
        return Remainder(dividend, divisor);
    }
    public static IntX operator /(IntX dividend, IntX divisor)
    {
        return Divide(dividend, divisor);
    }
    public ulong[] ToUIn64Array()
    {
        var al = Data.Length >> 1;
        if (al * 2 != Data.Length)
            al++;
        var arr = new ulong[al];
        Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2);
        return arr;
    }
    public uint[] ToUIn32Array()
    {
        return Data;
    }
    public byte[] ToByteArray()
    {
        var ba = new byte[Data.Length * DataSize];
        Buffer.BlockCopy(Data, 0, ba, 0, Data.Length * DataSize);
        return ba;
    }
    private void TrimToMsb()
    {
        var dataUsed = Data.Length;
        while (dataUsed > 1 && Data[dataUsed - 1] == 0)
            --dataUsed;
        if (dataUsed != Data.Length)
        {
            var tData = new uint[dataUsed];
            for (var i = 0; i < dataUsed; i++)
                tData[i] = Data[i];
            Data = (uint[]) tData.Clone();
        }
    }
    public static IntX Multiply(IntX left, IntX right)
    {
        if (left == 0 || right == 0)
            return Zero;
        if (left == 1 && right != 1)
            return right;
        if (left != 1 && right == 1)
            return left;
        if (left == 1 && right == 1)
            return One;
        var xInts   = left.Data;
        var yInts   = right.Data;
        var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
        for (var i = 0; i < xInts.Length; i++)
        {
            var   index     = i;
            ulong remainder = 0;
            foreach (var yi in yInts)
            {
                remainder        = remainder + (ulong) xInts[i] * yi + mulInts[index];
                mulInts[index++] = (uint) remainder;
                remainder        = remainder >> 32;
            }
            while (remainder != 0)
            {
                remainder        += mulInts[index];
                mulInts[index++] =  (uint) remainder;
                remainder        =  remainder >> 32;
            }
        }
        return new IntX(left.Sign * right.Sign, mulInts, DataBitWidth);
    }
    public static IntX operator *(IntX left, IntX right)
    {
        return Multiply(left, right);
    }
    public static IntX operator >>(IntX value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value << int.MaxValue << 1;
        if (shift < 0)
            return value << -shift;
        var xd          = value.Data;
        var shiftAmount = 32;
        var invShift    = 0;
        var bufLen      = xd.Length;
        while (bufLen > 1 && xd[bufLen - 1] == 0)
            bufLen--;
        for (var count = shift; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
            {
                shiftAmount = count;
                invShift    = 32 - shiftAmount;
            }
            ulong carry = 0;
            for (var i = bufLen - 1; i >= 0; i--)
            {
                var val = (ulong) xd[i] >> shiftAmount;
                val   |= carry;
                carry =  (ulong) xd[i] << invShift;
                xd[i] =  (uint) val;
            }
        }
        return new IntX(value.Sign, xd, DataBitWidth);
    }
    public static IntX operator <<(IntX value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value >> int.MaxValue >> 1;
        if (shift < 0)
            return value >> -shift;
        var digitShift = shift / 32;
        var smallShift = shift - digitShift * 32;
        var xd         = value.Data;
        var xl         = xd.Length;
        var zd         = new uint[xl + digitShift + 1];
        if (smallShift == 0)
        {
            for (var index = 0; index < xl; ++index)
                zd[index + digitShift] = xd[index];
        }
        else
        {
            var  carryShift = 32 - smallShift;
            uint carry      = 0;
            int  index;
            for (index = 0; index < xl; ++index)
            {
                var rot = xd[index];
                zd[index + digitShift] = (rot << smallShift) | carry;
                carry                  = rot >> carryShift;
            }
            zd[index + digitShift] = carry;
        }
        return new IntX(value.Sign, zd, DataBitWidth);
    }
    public static IntX operator |(IntX left, IntX right)
    {
        if (left == 0)
            return right;
        if (right == 0)
            return left;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu | yu;
        }
        return new IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static IntX operator ^(IntX left, IntX right)
    {
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu ^ yu;
        }
        return new IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static IntX operator &(IntX left, IntX right)
    {
        if (left == 0 || right == 0)
            return 0;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu & yu;
        }
        return new IntX(left.Sign * right.Sign, z, DataBitWidth);
    }
    public string ToBinaryString()
    {
        var bytes  = ToByteArray();
        var index  = bytes.Length - 1;
        var base2  = new StringBuilder(bytes.Length * 8);
        var binary = Convert.ToString(bytes[index], 2);
        if (binary[0] != '0' && Sign == 1) base2.Append('0');
        base2.Append(binary);
        for (index--; index >= 0; index--)
            base2.Append(Convert.ToString(bytes[index], 2).PadLeft(8, '0'));
        return base2.ToString();
    }
    public string ToOctalString()
    {
        var bytes         = ToByteArray();
        var index         = bytes.Length - 1;
        var base8         = new StringBuilder((bytes.Length / 3 + 1) * 8);
        var rem           = bytes.Length % 3;
        if (rem == 0) rem = 3;
        var base0         = 0;
        while (rem != 0)
        {
            base0 <<= 8;
            base0 +=  bytes[index--];
            rem--;
        }
        var octal = Convert.ToString(base0, 8);
        if (octal[0] != '0' && Sign == 1) base8.Append('0');
        base8.Append(octal);
        while (index >= 0)
        {
            base0 = (bytes[index] << 16) + (bytes[index - 1] << 8) + bytes[index - 2];
            base8.Append(Convert.ToString(base0, 8).PadLeft(8, '0'));
            index -= 3;
        }
        return base8.ToString();
    }
    public static IntX Pow(IntX value, IntX exponent, int bitLength)
    {
        if (value == null)
            throw new ArgumentNullException("Value cannot be null");
        if (exponent == null)
            throw new ArgumentNullException("Exponent cannot be null");
        if (exponent < 0)
            throw new ArgumentOutOfRangeException("Exponent", "Exponent cannot be negative");
        var result = new IntX("1", bitLength);
        while (exponent != 0)
        {
            if ((exponent & 1) != 0)
                result *= value;
            exponent >>= 1;
            value    *=  value;
        }
        return result;
    }
    public static IntX ModPow(IntX n, IntX e, IntX m)
    {
        var n1 = new IntX(n, DataBitWidth);
        var e1 = new IntX(e, DataBitWidth);
        var r  = new IntX(1, DataBitWidth);
        while (e1 != 0)
        {
            if (e1 % 2 == 1)
                r = r * n1 % m;
            e1 >>= 1;
            n1 =   n1 * n1 % m;
        }
        return r;
    }
    public static int GetSign(uint[] value)
    {
        var allZero = true;
        for (var i = 0; i < value.Length; i++)
            if (value[i] != 0)
            {
                allZero = false;
                break;
            }
        if (allZero)
            return 0;
        return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1;
    }
    private static int GetDataUsed(uint[] array)
    {
        var neg      = GetSign(array) < 0;
        var dataUsed = array.Length;
        if (!neg)
        {
            while (dataUsed > 1 && array[dataUsed - 1] == 0)
                --dataUsed;
            if (dataUsed == 0)
                dataUsed = 1;
        }
        return dataUsed;
    }
    public int GetDecimalPlaces()
    {
        var dPlaces = 0;
        if (Sign == 0)
            return 1;
        var a = new IntX(this, DataBitWidth);
        if (Sign < 0)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
                return 0;
            }
        var biRadix = new IntX(10, DataBitWidth);
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                a = quotient;
                dPlaces++;
            }
            catch (Exception ex)
            {
                break;
            }
        return dPlaces;
    }
    public (IntX approximateRoot, BigRational realRoot) Sqrt()
    {
        var n = (BigRational)this;
        var r = n.Sqrt();
        return (r.GetWholePart, r);
    }
    private uint[] TwosComplement(uint[] d)
    {
        var  i = 0;
        uint v = 0;
        for (; i < d.Length; i++)
        {
            v    = ~d[i] + 1;
            d[i] = v;
            if (v != 0)
            {
                i++;
                break;
            }
        }
        if (v != 0)
        {
            for (; i < d.Length; i++)
                d[i] = ~d[i];
        }
        else
        {
            Array.Resize(ref d, d.Length + 1);
            d[d.Length - 1] = 1;
        }
        return d;
    }    
    public IntX Pow(int e)
    {
        var ans = this;
        if (e == 1)
            return ans;
        if (e == 0)
            return 1;
        for (var i = 1; i != e; i++)
            ans *= this;
        return ans;
    }
    public static double Log(IntX value, double baseValue)
    {
        var c          = 0.0;
        var d          = 0.5;
        var dataLength = value.DataUsed;
        var topBits    = 0;
        var x          = value.Data[dataLength - 1];
        while (x > 0)
        {
            x >>= 1;
            topBits++;
        }
        var bitLength = (dataLength - 1) * 32 + topBits;
        var bit       = (uint) (1 << (topBits - 1));
        for (var index = dataLength - 1; index >= 0; --index)
        {
            for (; bit != 0U; bit >>= 1)
            {
                if (((int) value.Data[index] & (int) bit) != 0)
                    c += d;
                d *= 0.5;
            }
            bit = 2147483648U;
        }
        return (Math.Log(c) + 0.69314718055994530941723212145818 * bitLength) / Math.Log(baseValue);
    }
    public static List<IntX> GetFactors(IntX n)
    {
        var Factors = new List<IntX>();
        var s       = (IntX) 1 << ((int) Math.Ceiling(Log(n, 2)) >> 1);
        var a       = Three;
        while (a < s)
        {
            if (n % a == 0)
            {
                Factors.Add(a);
                if (a * a != n)
                    Factors.Add(n / a);
            }
            a += 2;
        }
        return Factors;
    }
    public static IntX GreatestCommonDivisor(IntX a, IntX b)
    {
        while (b > 0)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static IntX LeastCommonMultiple(IntX a, IntX b)
    {
        return a * b / a.Gcd(b);
    }
    public static double Log10(IntX value)
    {
        return Log(value, 10.0);
    }
    public static double LogN(IntX value)
    {
        return Log(value, 2.0);
    }
    public void ConstructFromArray(byte[] value, int bitLength)
    {
        var minSize = value.Length / DataSize;
        if (value == null)
            throw new ArgumentNullException("value");
        DataBitWidth = bitLength;
        DataLength = DataBitWidth >> 5;
        var byteCount = value.Length;
        var isNegative = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |= value[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |= value[curByte];
            }
        }
    }
    private class IntXConverter : 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 IntX(0, DataBitWidth);
        }
        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);
        }
    }
}
public class IntXComparer : IComparer<IntX>
{
    public int Compare(IntX left, IntX right)
    {
        return left.CompareTo(right);
    }
    public bool Equals(IntX left, IntX right)
    {
        if (left == null || right == null)
            return false;
        return left.Equals(right);
    }
    public int GetHashCode(IntX obj)
    {
        return obj.GetHashCode();
    }
}

(Obsolete) UIntX.cs

Adjustable Bit Width Unsigned Integer 32,64,128,256,512,1024,2048…

Why not just use BigInteger? Think Hashing which relies on overflow.

Jun-11,2021: Obsolete Use xIntX Instead.

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

LinqHelper.cs

Custom Linq Range Class

using System;
using System.Collections.Generic;
using System.Numerics;
public static class LinqHelper
{
/// <summary>
/// Generates a sequence of character values within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<char> Range(char from, char to, char step = '\x0001')
{
var d = '\x0';
while((char) (from + (uint) d) < to)
{
yield return(char) (from + (uint) d);
d += step;
}
}
/// <summary>
/// Generates a sequence of 8-bit signed numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<sbyte> Range(sbyte from, sbyte to, sbyte step = 1)
{
var d = 0;
while((sbyte) (from + d) < to)
{
yield return(sbyte) (from + d);
d += step;
}
}
/// <summary>
/// Generates a sequence of 8-bit unsigned numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<byte> Range(byte from, byte to, byte step = 1)
{
var d = 0;
while((byte) (from + d) < to)
{
yield return(byte) (from + d);
d += step;
}
}
/// <summary>
/// Generates a sequence of 16-bit signed numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<short> Range(short from, short to, short step = 1)
{
var d = 0;
while((short) (from + d) < to)
{
yield return(short) (from + d);
d += step;
}
}
/// <summary>
/// Generates a sequence of 16-bit unsigned numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<ushort> Range(ushort from, ushort to, ushort step = 1)
{
var d = 0;
while((ushort) (from + d) < to)
{
yield return(ushort) (from + d);
d += step;
}
}
/// <summary>
/// Generates a sequence of 32-bit signed numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<int> Range(int from, int to, int step = 1)
{
var d = 0;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of 32-bit unsigned numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<uint> Range(uint from, uint to, uint step = 1)
{
var d = 0u;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of 64-bit signed numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<long> Range(long from, long to, long step = 1)
{
var d = 0l;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of 64-bit unsigned numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<ulong> Range(ulong from, ulong to, ulong step = 1)
{
var d = 0ul;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of arbitrarily large signed numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<BigInteger> Range(BigInteger from, BigInteger to, BigInteger step)
{
var count = to - from;
for(var i = BigInteger.Zero; i < count; i = i + step)
yield return from + i;
}
/// <summary>
/// Generates a sequence of arbitrarily large floating point precision numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<BigRational> Range(BigRational from, BigRational to, BigRational step)
{
if(step == 0)
step = 1;
var count = to - from;
for(BigRational i = 0; i < count; i = +step)
yield return from + i;
}
/// <summary>
/// Generates a sequence of single precision floating-point numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<float> Range(float from, float to, float step = 1)
{
var d = 0f;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of double precision floating-point numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<double> Range(double from, double to, double step = 1)
{
var d = 0d;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of decimal numbers within a specified range starting at
/// 'form' through 'to' with a given step value.
/// </summary>
public static IEnumerable<decimal> Range(decimal from, decimal to, decimal step = 1)
{
decimal d = 0;
while(from + d < to)
{
yield return from + d;
d += step;
}
}
/// <summary>
/// Generates a sequence of DateTime numbers within a specified range starting at
/// 'form' through 'to' with a given step value in days.
/// </summary>
public static IEnumerable<DateTime> Range(DateTime from, DateTime to, double step = 1)
{
var d = from;
while(d < to)
{
yield return d;
d = d.AddDays(step);
}
}
}
using System; using System.Collections.Generic; using System.Numerics; public static class LinqHelper { /// <summary> /// Generates a sequence of character values within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<char> Range(char from, char to, char step = '\x0001') { var d = '\x0'; while((char) (from + (uint) d) < to) { yield return(char) (from + (uint) d); d += step; } } /// <summary> /// Generates a sequence of 8-bit signed numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<sbyte> Range(sbyte from, sbyte to, sbyte step = 1) { var d = 0; while((sbyte) (from + d) < to) { yield return(sbyte) (from + d); d += step; } } /// <summary> /// Generates a sequence of 8-bit unsigned numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<byte> Range(byte from, byte to, byte step = 1) { var d = 0; while((byte) (from + d) < to) { yield return(byte) (from + d); d += step; } } /// <summary> /// Generates a sequence of 16-bit signed numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<short> Range(short from, short to, short step = 1) { var d = 0; while((short) (from + d) < to) { yield return(short) (from + d); d += step; } } /// <summary> /// Generates a sequence of 16-bit unsigned numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<ushort> Range(ushort from, ushort to, ushort step = 1) { var d = 0; while((ushort) (from + d) < to) { yield return(ushort) (from + d); d += step; } } /// <summary> /// Generates a sequence of 32-bit signed numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<int> Range(int from, int to, int step = 1) { var d = 0; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of 32-bit unsigned numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<uint> Range(uint from, uint to, uint step = 1) { var d = 0u; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of 64-bit signed numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<long> Range(long from, long to, long step = 1) { var d = 0l; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of 64-bit unsigned numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<ulong> Range(ulong from, ulong to, ulong step = 1) { var d = 0ul; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of arbitrarily large signed numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<BigInteger> Range(BigInteger from, BigInteger to, BigInteger step) { var count = to - from; for(var i = BigInteger.Zero; i < count; i = i + step) yield return from + i; } /// <summary> /// Generates a sequence of arbitrarily large floating point precision numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<BigRational> Range(BigRational from, BigRational to, BigRational step) { if(step == 0) step = 1; var count = to - from; for(BigRational i = 0; i < count; i = +step) yield return from + i; } /// <summary> /// Generates a sequence of single precision floating-point numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<float> Range(float from, float to, float step = 1) { var d = 0f; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of double precision floating-point numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<double> Range(double from, double to, double step = 1) { var d = 0d; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of decimal numbers within a specified range starting at /// 'form' through 'to' with a given step value. /// </summary> public static IEnumerable<decimal> Range(decimal from, decimal to, decimal step = 1) { decimal d = 0; while(from + d < to) { yield return from + d; d += step; } } /// <summary> /// Generates a sequence of DateTime numbers within a specified range starting at /// 'form' through 'to' with a given step value in days. /// </summary> public static IEnumerable<DateTime> Range(DateTime from, DateTime to, double step = 1) { var d = from; while(d < to) { yield return d; d = d.AddDays(step); } } }
using System;
using System.Collections.Generic;
using System.Numerics;
public static class LinqHelper
{
    /// <summary>
    ///     Generates a sequence of character values within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<char> Range(char from, char to, char step = '\x0001')
    {
        var d = '\x0';
        while((char) (from + (uint) d) < to)
        {
            yield return(char) (from + (uint) d);
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 8-bit signed numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<sbyte> Range(sbyte from, sbyte to, sbyte step = 1)
    {
        var d = 0;
        while((sbyte) (from + d) < to)
        {
            yield return(sbyte) (from + d);
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 8-bit unsigned numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<byte> Range(byte from, byte to, byte step = 1)
    {
        var d = 0;
        while((byte) (from + d) < to)
        {
            yield return(byte) (from + d);
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 16-bit signed numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<short> Range(short from, short to, short step = 1)
    {
        var d = 0;
        while((short) (from + d) < to)
        {
            yield return(short) (from + d);
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 16-bit unsigned numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<ushort> Range(ushort from, ushort to, ushort step = 1)
    {
        var d = 0;
        while((ushort) (from + d) < to)
        {
            yield return(ushort) (from + d);
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 32-bit signed numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<int> Range(int from, int to, int step = 1)
    {
        var d = 0;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 32-bit unsigned numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<uint> Range(uint from, uint to, uint step = 1)
    {
        var d = 0u;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 64-bit signed numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<long> Range(long from, long to, long step = 1)
    {
        var d = 0l;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of 64-bit unsigned numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<ulong> Range(ulong from, ulong to, ulong step = 1)
    {
        var d = 0ul;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of arbitrarily large signed numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<BigInteger> Range(BigInteger from, BigInteger to, BigInteger step)
    {
        var count = to - from;
        for(var i = BigInteger.Zero; i < count; i = i + step)
            yield return from + i;
    }
    /// <summary>
    ///     Generates a sequence of arbitrarily large floating point precision numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<BigRational> Range(BigRational from, BigRational to, BigRational step)
    {
        if(step == 0)
            step = 1;
        var count = to - from;
        for(BigRational i = 0; i < count; i = +step)
            yield return from + i;
    }
    /// <summary>
    ///     Generates a sequence of single precision floating-point numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<float> Range(float from, float to, float step = 1)
    {
        var d = 0f;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of double precision floating-point numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<double> Range(double from, double to, double step = 1)
    {
        var d = 0d;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of decimal numbers within a specified range starting at
    ///     'form' through 'to' with a given step value.
    /// </summary>
    public static IEnumerable<decimal> Range(decimal from, decimal to, decimal step = 1)
    {
        decimal d = 0;
        while(from + d < to)
        {
            yield return from + d;
            d += step;
        }
    }
    /// <summary>
    ///     Generates a sequence of DateTime numbers within a specified range starting at
    ///     'form' through 'to' with a given step value in days.
    /// </summary>
    public static IEnumerable<DateTime> Range(DateTime from, DateTime to, double step = 1)
    {
        var d = from;
        while(d < to)
        {
            yield return d;
            d = d.AddDays(step);
        }
    }
}

BigIntegerPrime.cs

Generate or Test BigInteger Primes

using System;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
public class BigIntegerPrime
{
private readonly int _bitWidth;
private readonly int[] _lowPrimes =
{
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881,
883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
};
private readonly BigIntegerRng _rng;
private readonly BigInteger _twosixtyfour = "18446744073709551616".BigIntegerBase10();
private readonly uint[] _w0 = {2};
private readonly uint[] _w1 = {2, 3};
private readonly uint[] _w10 = {2, 3, 5, 7, 11, 13, 17, 19, 23};
private readonly uint[] _w11 = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
private readonly uint[] _w12 = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
private readonly uint[] _w2 = {31, 73};
private readonly uint[] _w3 = {2, 3, 5};
private readonly uint[] _w4 = {2, 3, 5, 7};
private readonly uint[] _w5 = {2, 7, 61};
private readonly uint[] _w6 = {2, 13, 23, 1662803};
private readonly uint[] _w7 = {2, 3, 5, 7, 11};
private readonly uint[] _w8 = {2, 3, 5, 7, 11, 13};
private readonly uint[] _w9 = {2, 3, 5, 7, 11, 13, 17};
private BigInteger _nextPrime;
public BigIntegerPrime(int BitWidthHint)
{
_rng = new BigIntegerRng(BitWidthHint);
_rng.Unsigned = true;
_rng.OddsOnly = true;
_bitWidth = BitWidthHint;
}
public bool IsPrime(BigInteger candidate)
{
if (candidate == 1)
return false;
if (candidate == 2 || candidate == 3 || candidate == 5)
return true;
if (candidate <= 1000)
return CheckLowPrimes(candidate);
if (!PrimeCheckM10LD(candidate))
return false;
if (!PrimeCheckM6(candidate))
return false;
if (!TrialDivision(candidate))
return false;
if (candidate < 2047)
{
if (!MillerRabin(candidate, _w0))
return false;
}
else if (candidate > 2047 && candidate <= 1373653)
{
if (!MillerRabin(candidate, _w1))
return false;
}
else if (candidate > 1373653 && candidate <= 9080191)
{
if (!MillerRabin(candidate, _w2))
return false;
}
else if (candidate > 9080191 && candidate <= 25326001)
{
if (!MillerRabin(candidate, _w3))
return false;
}
else if (candidate > 25326001 && candidate <= 3215031751)
{
if (!MillerRabin(candidate, _w4))
return false;
}
else if (candidate > 3215031751 && candidate <= 4759123141)
{
if (!MillerRabin(candidate, _w5))
return false;
}
else if (candidate > 4759123141 && candidate <= 1122004669633)
{
if (!MillerRabin(candidate, _w6))
return false;
}
else if (candidate > 1122004669633 && candidate <= 2152302898747)
{
if (!MillerRabin(candidate, _w7))
return false;
}
else if (candidate > 2152302898747 && candidate <= 3474749660383)
{
if (!MillerRabin(candidate, _w8))
return false;
}
else if (candidate > 3474749660383 && candidate <= 341550071728321)
{
if (!MillerRabin(candidate, _w9))
return false;
}
else if (candidate > 341550071728321 && candidate <= 3825123056546413051)
{
if (!MillerRabin(candidate, _w10))
return false;
}
else if (candidate > 3825123056546413051 && candidate < _twosixtyfour)
{
if (!MillerRabin(candidate, _w11))
return false;
}
else if (candidate > _twosixtyfour)
{
if (!MillerRabin(candidate, _w12))
return false;
}
return true;
}
private bool MillerRabin(BigInteger candidate, uint[] w)
{
var s = 0;
var d = candidate - BigInteger.One;
while ((d & 1) == 0)
{
d >>= 1;
s++;
}
if (s == 0)
return false;
var nmo = candidate - BigInteger.One;
for (var i = 0; i < w.Length; ++i)
{
BigInteger a;
if (candidate > _twosixtyfour)
a = _rng.Next(3, nmo);
else
a = w[i];
var x = BigInteger.ModPow(a, d, candidate);
if (x == 1 || x == nmo)
continue;
for (var r = 1; r < s; ++r)
{
x = BigInteger.ModPow(x, 2, candidate);
if (x == 1)
return false;
if (x == nmo)
break;
}
if (x == nmo)
continue;
return false;
}
return true;
}
private bool TrialDivision(BigInteger candidate)
{
for (var i = 0; i < _lowPrimes.Length; i++)
{
var p = _lowPrimes[i];
if (i < p)
{
if (candidate % p != 0)
continue;
return false;
}
break;
}
return true;
}
private static bool PrimeCheckM10LD(BigInteger n)
{
var d1 = (int) (n % 10);
return d1 == 1 || d1 == 3 || d1 == 7 || d1 == 9;
}
private static bool PrimeCheckM6(BigInteger n)
{
var d1 = (int) (n % 6);
return d1 == 1 || d1 == 5;
}
private bool CheckLowPrimes(BigInteger val)
{
foreach (var v in _lowPrimes)
if (val == v)
return true;
return false;
}
private void NextPrime(int t)
{
if (t > Environment.ProcessorCount - 1)
return;
var MaxValue = (BigInteger.One << _bitWidth) - 1;
while (true)
{
if (_nextPrime != 0)
return;
var n = _rng.NextFast(_bitWidth);
if (_nextPrime != 0)
return;
if (n == 1)
continue;
if (n > MaxValue)
continue;
if (!IsPrime(n))
continue;
_nextPrime = n;
}
}
public BigInteger GetPrime()
{
void invoke()
{
Parallel.Invoke(new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount},
() => NextPrime(1),
() => NextPrime(2),
() => NextPrime(3),
() => NextPrime(4),
() => NextPrime(5),
() => NextPrime(6),
() => NextPrime(7),
() => NextPrime(8),
() => NextPrime(9),
() => NextPrime(10),
() => NextPrime(11),
() => NextPrime(12),
() => NextPrime(13),
() => NextPrime(14),
() => NextPrime(15),
() => NextPrime(16)
);
}
_nextPrime = 0;
var thread = new Thread(invoke) {Priority = ThreadPriority.Highest};
thread.Start();
thread.Join();
return _nextPrime;
}
}
using System; using System.Numerics; using System.Threading; using System.Threading.Tasks; public class BigIntegerPrime { private readonly int _bitWidth; private readonly int[] _lowPrimes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 }; private readonly BigIntegerRng _rng; private readonly BigInteger _twosixtyfour = "18446744073709551616".BigIntegerBase10(); private readonly uint[] _w0 = {2}; private readonly uint[] _w1 = {2, 3}; private readonly uint[] _w10 = {2, 3, 5, 7, 11, 13, 17, 19, 23}; private readonly uint[] _w11 = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; private readonly uint[] _w12 = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41}; private readonly uint[] _w2 = {31, 73}; private readonly uint[] _w3 = {2, 3, 5}; private readonly uint[] _w4 = {2, 3, 5, 7}; private readonly uint[] _w5 = {2, 7, 61}; private readonly uint[] _w6 = {2, 13, 23, 1662803}; private readonly uint[] _w7 = {2, 3, 5, 7, 11}; private readonly uint[] _w8 = {2, 3, 5, 7, 11, 13}; private readonly uint[] _w9 = {2, 3, 5, 7, 11, 13, 17}; private BigInteger _nextPrime; public BigIntegerPrime(int BitWidthHint) { _rng = new BigIntegerRng(BitWidthHint); _rng.Unsigned = true; _rng.OddsOnly = true; _bitWidth = BitWidthHint; } public bool IsPrime(BigInteger candidate) { if (candidate == 1) return false; if (candidate == 2 || candidate == 3 || candidate == 5) return true; if (candidate <= 1000) return CheckLowPrimes(candidate); if (!PrimeCheckM10LD(candidate)) return false; if (!PrimeCheckM6(candidate)) return false; if (!TrialDivision(candidate)) return false; if (candidate < 2047) { if (!MillerRabin(candidate, _w0)) return false; } else if (candidate > 2047 && candidate <= 1373653) { if (!MillerRabin(candidate, _w1)) return false; } else if (candidate > 1373653 && candidate <= 9080191) { if (!MillerRabin(candidate, _w2)) return false; } else if (candidate > 9080191 && candidate <= 25326001) { if (!MillerRabin(candidate, _w3)) return false; } else if (candidate > 25326001 && candidate <= 3215031751) { if (!MillerRabin(candidate, _w4)) return false; } else if (candidate > 3215031751 && candidate <= 4759123141) { if (!MillerRabin(candidate, _w5)) return false; } else if (candidate > 4759123141 && candidate <= 1122004669633) { if (!MillerRabin(candidate, _w6)) return false; } else if (candidate > 1122004669633 && candidate <= 2152302898747) { if (!MillerRabin(candidate, _w7)) return false; } else if (candidate > 2152302898747 && candidate <= 3474749660383) { if (!MillerRabin(candidate, _w8)) return false; } else if (candidate > 3474749660383 && candidate <= 341550071728321) { if (!MillerRabin(candidate, _w9)) return false; } else if (candidate > 341550071728321 && candidate <= 3825123056546413051) { if (!MillerRabin(candidate, _w10)) return false; } else if (candidate > 3825123056546413051 && candidate < _twosixtyfour) { if (!MillerRabin(candidate, _w11)) return false; } else if (candidate > _twosixtyfour) { if (!MillerRabin(candidate, _w12)) return false; } return true; } private bool MillerRabin(BigInteger candidate, uint[] w) { var s = 0; var d = candidate - BigInteger.One; while ((d & 1) == 0) { d >>= 1; s++; } if (s == 0) return false; var nmo = candidate - BigInteger.One; for (var i = 0; i < w.Length; ++i) { BigInteger a; if (candidate > _twosixtyfour) a = _rng.Next(3, nmo); else a = w[i]; var x = BigInteger.ModPow(a, d, candidate); if (x == 1 || x == nmo) continue; for (var r = 1; r < s; ++r) { x = BigInteger.ModPow(x, 2, candidate); if (x == 1) return false; if (x == nmo) break; } if (x == nmo) continue; return false; } return true; } private bool TrialDivision(BigInteger candidate) { for (var i = 0; i < _lowPrimes.Length; i++) { var p = _lowPrimes[i]; if (i < p) { if (candidate % p != 0) continue; return false; } break; } return true; } private static bool PrimeCheckM10LD(BigInteger n) { var d1 = (int) (n % 10); return d1 == 1 || d1 == 3 || d1 == 7 || d1 == 9; } private static bool PrimeCheckM6(BigInteger n) { var d1 = (int) (n % 6); return d1 == 1 || d1 == 5; } private bool CheckLowPrimes(BigInteger val) { foreach (var v in _lowPrimes) if (val == v) return true; return false; } private void NextPrime(int t) { if (t > Environment.ProcessorCount - 1) return; var MaxValue = (BigInteger.One << _bitWidth) - 1; while (true) { if (_nextPrime != 0) return; var n = _rng.NextFast(_bitWidth); if (_nextPrime != 0) return; if (n == 1) continue; if (n > MaxValue) continue; if (!IsPrime(n)) continue; _nextPrime = n; } } public BigInteger GetPrime() { void invoke() { Parallel.Invoke(new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount}, () => NextPrime(1), () => NextPrime(2), () => NextPrime(3), () => NextPrime(4), () => NextPrime(5), () => NextPrime(6), () => NextPrime(7), () => NextPrime(8), () => NextPrime(9), () => NextPrime(10), () => NextPrime(11), () => NextPrime(12), () => NextPrime(13), () => NextPrime(14), () => NextPrime(15), () => NextPrime(16) ); } _nextPrime = 0; var thread = new Thread(invoke) {Priority = ThreadPriority.Highest}; thread.Start(); thread.Join(); return _nextPrime; } }
using System;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
public class BigIntegerPrime
{
    private readonly int _bitWidth;
    private readonly int[] _lowPrimes =
    {
        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
        157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
        317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
        499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677,
        683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881,
        883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
    };
    private readonly BigIntegerRng _rng;
    private readonly BigInteger    _twosixtyfour = "18446744073709551616".BigIntegerBase10();
    private readonly uint[]        _w0           = {2};
    private readonly uint[]        _w1           = {2, 3};
    private readonly uint[]        _w10          = {2, 3, 5, 7, 11, 13, 17, 19, 23};
    private readonly uint[]        _w11          = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    private readonly uint[]        _w12          = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
    private readonly uint[]        _w2           = {31, 73};
    private readonly uint[]        _w3           = {2, 3, 5};
    private readonly uint[]        _w4           = {2, 3, 5, 7};
    private readonly uint[]        _w5           = {2, 7, 61};
    private readonly uint[]        _w6           = {2, 13, 23, 1662803};
    private readonly uint[]        _w7           = {2, 3, 5, 7, 11};
    private readonly uint[]        _w8           = {2, 3, 5, 7, 11, 13};
    private readonly uint[]        _w9           = {2, 3, 5, 7, 11, 13, 17};
    private          BigInteger    _nextPrime;
    public BigIntegerPrime(int BitWidthHint)
    {
        _rng          = new BigIntegerRng(BitWidthHint);
        _rng.Unsigned = true;
        _rng.OddsOnly = true;
        _bitWidth     = BitWidthHint;
    }
    public bool IsPrime(BigInteger candidate)
    {
        if (candidate == 1)
            return false;
        if (candidate == 2 || candidate == 3 || candidate == 5)
            return true;
        if (candidate <= 1000)
            return CheckLowPrimes(candidate);
        if (!PrimeCheckM10LD(candidate))
            return false;
        if (!PrimeCheckM6(candidate))
            return false;
        if (!TrialDivision(candidate))
            return false;
        if (candidate < 2047)
        {
            if (!MillerRabin(candidate, _w0))
                return false;
        }
        else if (candidate > 2047 && candidate <= 1373653)
        {
            if (!MillerRabin(candidate, _w1))
                return false;
        }
        else if (candidate > 1373653 && candidate <= 9080191)
        {
            if (!MillerRabin(candidate, _w2))
                return false;
        }
        else if (candidate > 9080191 && candidate <= 25326001)
        {
            if (!MillerRabin(candidate, _w3))
                return false;
        }
        else if (candidate > 25326001 && candidate <= 3215031751)
        {
            if (!MillerRabin(candidate, _w4))
                return false;
        }
        else if (candidate > 3215031751 && candidate <= 4759123141)
        {
            if (!MillerRabin(candidate, _w5))
                return false;
        }
        else if (candidate > 4759123141 && candidate <= 1122004669633)
        {
            if (!MillerRabin(candidate, _w6))
                return false;
        }
        else if (candidate > 1122004669633 && candidate <= 2152302898747)
        {
            if (!MillerRabin(candidate, _w7))
                return false;
        }
        else if (candidate > 2152302898747 && candidate <= 3474749660383)
        {
            if (!MillerRabin(candidate, _w8))
                return false;
        }
        else if (candidate > 3474749660383 && candidate <= 341550071728321)
        {
            if (!MillerRabin(candidate, _w9))
                return false;
        }
        else if (candidate > 341550071728321 && candidate <= 3825123056546413051)
        {
            if (!MillerRabin(candidate, _w10))
                return false;
        }
        else if (candidate > 3825123056546413051 && candidate < _twosixtyfour)
        {
            if (!MillerRabin(candidate, _w11))
                return false;
        }
        else if (candidate > _twosixtyfour)
        {
            if (!MillerRabin(candidate, _w12))
                return false;
        }
        return true;
    }
    private bool MillerRabin(BigInteger candidate, uint[] w)
    {
        var s = 0;
        var d = candidate - BigInteger.One;
        while ((d & 1) == 0)
        {
            d >>= 1;
            s++;
        }
        if (s == 0)
            return false;
        var nmo = candidate - BigInteger.One;
        for (var i = 0; i < w.Length; ++i)
        {
            BigInteger a;
            if (candidate > _twosixtyfour)
                a = _rng.Next(3, nmo);
            else
                a = w[i];
            var x = BigInteger.ModPow(a, d, candidate);
            if (x == 1 || x == nmo)
                continue;
            for (var r = 1; r < s; ++r)
            {
                x = BigInteger.ModPow(x, 2, candidate);
                if (x == 1)
                    return false;
                if (x == nmo)
                    break;
            }
            if (x == nmo)
                continue;
            return false;
        }
        return true;
    }
    private bool TrialDivision(BigInteger candidate)
    {
        for (var i = 0; i < _lowPrimes.Length; i++)
        {
            var p = _lowPrimes[i];
            if (i < p)
            {
                if (candidate % p != 0)
                    continue;
                return false;
            }
            break;
        }
        return true;
    }
    private static bool PrimeCheckM10LD(BigInteger n)
    {
        var d1 = (int) (n % 10);
        return d1 == 1 || d1 == 3 || d1 == 7 || d1 == 9;
    }
    private static bool PrimeCheckM6(BigInteger n)
    {
        var d1 = (int) (n % 6);
        return d1 == 1 || d1 == 5;
    }
    private bool CheckLowPrimes(BigInteger val)
    {
        foreach (var v in _lowPrimes)
            if (val == v)
                return true;
        return false;
    }
    private void NextPrime(int t)
    {
        if (t > Environment.ProcessorCount - 1)
            return;
        var MaxValue = (BigInteger.One << _bitWidth) - 1;
        while (true)
        {
            if (_nextPrime != 0)
                return;
            var n = _rng.NextFast(_bitWidth);
            if (_nextPrime != 0)
                return;
            if (n == 1)
                continue;
            if (n > MaxValue)
                continue;
            if (!IsPrime(n))
                continue;
            _nextPrime = n;
        }
    }
    public BigInteger GetPrime()
    {
        void invoke()
        {
            Parallel.Invoke(new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount},
                () => NextPrime(1),
                () => NextPrime(2),
                () => NextPrime(3),
                () => NextPrime(4),
                () => NextPrime(5),
                () => NextPrime(6),
                () => NextPrime(7),
                () => NextPrime(8),
                () => NextPrime(9),
                () => NextPrime(10),
                () => NextPrime(11),
                () => NextPrime(12),
                () => NextPrime(13),
                () => NextPrime(14),
                () => NextPrime(15),
                () => NextPrime(16)
            );
        }
        _nextPrime = 0;
        var thread = new Thread(invoke) {Priority = ThreadPriority.Highest};
        thread.Start();
        thread.Join();
        return _nextPrime;
    }
}

BigDecimal.cs

Arbitrary Precision Signed BigDecimal

Updated: Jun-15,2021

using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[DebuggerDisplay("{DDisplay}")]
public struct BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal>
{
private const int MaxFactorials = 200;
private static int _bitWidth = 2048;
public static float MaxPrecision = _bitWidth / 64f * 20f;
private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, 308);
private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue;
private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue;
private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, 28);
private static readonly BigInteger DecimalMaxValue = (BigInteger)decimal.MaxValue;
private static readonly BigInteger DecimalMinValue = (BigInteger)decimal.MinValue;
private static BigDecimal[] Factorials;
private int _scale;
private BigInteger _unscaledValue;
public BigDecimal(BigInteger value) : this(value, 0)
{
}
public BigDecimal(xIntX value) : this((BigInteger)value, 0)
{
}
public BigDecimal(BigIntX value) : this((BigInteger)value, 0)
{
}
public BigDecimal(BigInteger value, int scale)
{
_unscaledValue = value;
_scale = scale;
}
public BigDecimal(long value) : this(value, 0)
{
}
public BigDecimal(double value) : this((decimal)value)
{
}
public BigDecimal(float value) : this((decimal)value)
{
}
public BigDecimal(byte[] value)
{
var number = new byte[value.Length - 4];
var flags = new byte[4];
Array.Copy(value, 0, number, 0, number.Length);
Array.Copy(value, value.Length - 4, flags, 0, 4);
_unscaledValue = new BigInteger(number);
_scale = BitConverter.ToInt32(flags, 0);
}
public BigDecimal(BigRational value)
{
var num = (BigDecimal)value.Numerator;
var den = (BigDecimal)value.Denominator;
var bigDecRes = num / den;
_unscaledValue = bigDecRes._unscaledValue;
_scale = bigDecRes.Scale;
}
public BigDecimal(BigDecimal value)
{
_unscaledValue = value._unscaledValue;
_scale = value.Scale;
}
public BigDecimal(decimal value)
{
var bytes = new byte[16];
var bits = decimal.GetBits(value);
var lo = bits[0];
var mid = bits[1];
var hi = bits[2];
var flags = bits[3];
bytes[0] = (byte)lo;
bytes[1] = (byte)(lo >> 8);
bytes[2] = (byte)(lo >> 0x10);
bytes[3] = (byte)(lo >> 0x18);
bytes[4] = (byte)mid;
bytes[5] = (byte)(mid >> 8);
bytes[6] = (byte)(mid >> 0x10);
bytes[7] = (byte)(mid >> 0x18);
bytes[8] = (byte)hi;
bytes[9] = (byte)(hi >> 8);
bytes[10] = (byte)(hi >> 0x10);
bytes[11] = (byte)(hi >> 0x18);
bytes[12] = (byte)flags;
bytes[13] = (byte)(flags >> 8);
bytes[14] = (byte)(flags >> 0x10);
bytes[15] = (byte)(flags >> 0x18);
var unscaledValueBytes = new byte[12];
Array.Copy(bytes, unscaledValueBytes, unscaledValueBytes.Length);
var unscaledValue = new BigInteger(unscaledValueBytes);
var scale = bytes[14];
if (bytes[15] == 128)
unscaledValue *= BigInteger.MinusOne;
_unscaledValue = unscaledValue;
_scale = scale;
}
public BigDecimal(string value)
{
if (!value.ContainsOnly("0123456789+-.eE"))
throw new Exception($"Input value must only contain these '0123456789+-.eE', value'{value}");
var len = value.Length;
var start = 0;
var point = 0;
var dot = -1;
var negative = false;
if (value[0] == '+')
{
++start;
++point;
}
else if (value[0] == '-')
{
++start;
++point;
negative = true;
}
while (point < len)
{
var c = value[point];
if (c == '.')
{
if (dot >= 0)
throw new Exception($"There are multiple '.'s in the value {value}");
dot = point;
}
else if (c == 'e' || c == 'E')
{
break;
}
++point;
}
string val;
if (dot >= 0)
{
val = value.Substring(start, dot) + value.Substring(dot + 1, point - (dot + 1));
_scale = point - 1 - dot;
}
else
{
val = value.Substring(start, point);
_scale = 0;
}
if (val.Length == 0)
throw new Exception($"There are no digits in the value {value}");
if (negative)
val = "-" + val;
_unscaledValue = val.BigIntegerBase10();
if (point < len)
try
{
point++;
switch (value[point])
{
case '+':
{
point++;
if (point >= len)
throw new Exception($"No exponent following e or E. Value {value}");
var scale = value.Substring(point);
_scale -= int.Parse(scale);
return;
}
case '-':
{
point++;
if (point >= len)
throw new Exception($"No exponent following e or E. Value {value}");
var scale = value.Substring(point);
_scale += int.Parse(scale);
return;
}
default:
throw new Exception($"Malformed exponent in value {value}");
}
}
catch (Exception ex)
{
throw new Exception($"Malformed exponent in value {value}");
}
}
public int BitWidth
{
get => _bitWidth;
set
{
_bitWidth = value;
MaxPrecision = _bitWidth / 64f * 20f;
}
}
private string DDisplay => ToString();
public static BigDecimal Zero
{
get;
} = new(BigInteger.Zero);
public static BigDecimal One
{
get;
} = new(BigInteger.One);
public static BigDecimal MinusOne
{
get;
} = new(BigInteger.MinusOne);
public bool IsEven => _unscaledValue.IsEven;
public bool IsOne => _unscaledValue.IsOne;
public bool IsPowerOfTwo => _unscaledValue.IsPowerOfTwo;
public bool IsZero => _unscaledValue.IsZero;
public int Sign => _unscaledValue.Sign;
public int Scale
{
get => _scale;
private set => _scale = value;
}
public BigInteger UnscaledValue => _unscaledValue;
public BigDecimal WholePart => BigInteger.Divide(_unscaledValue, BigInteger.Pow(10, Scale));
public BigDecimal FractionalPart => this - WholePart;
public int DecimalPlaces
{
get
{
var a = new BigDecimal(_unscaledValue);
var dPlaces = 0;
if (a.Sign == 0)
return 1;
if (a.Sign < 0)
try
{
a = -a;
}
catch (Exception ex)
{
return 0;
}
var biRadix = new BigDecimal(10);
while (a > 0)
try
{
a /= biRadix;
dPlaces++;
}
catch (Exception ex)
{
break;
}
return dPlaces;
}
}
int IComparable.CompareTo(object obj)
{
if (obj == null)
return 1;
if (!(obj is BigDecimal))
throw new Exception("Argument must be of type BigDecimal.");
return Compare(this, (BigDecimal)obj);
}
public int CompareTo(BigDecimal other)
{
return Compare(this, other);
}
public bool Equals(BigDecimal other)
{
return _unscaledValue == other._unscaledValue && _scale == other.Scale;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (!(obj is BigDecimal))
return false;
return Equals((BigDecimal)obj);
}
public static BigDecimal Round(BigDecimal number, int decimalPlaces)
{
BigDecimal power = BigInteger.Pow(10, decimalPlaces);
number *= power;
return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
}
public void Round(int decimalPlaces)
{
var number = this;
BigDecimal power = BigInteger.Pow(10, decimalPlaces);
number *= power;
var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
_unscaledValue = n._unscaledValue;
_scale = n.Scale;
}
public override int GetHashCode()
{
return UnscaledValue.GetHashCode() ^ _scale.GetHashCode();
}
public override string ToString()
{
return ToStringInt();
}
public string ToString(IFormatProvider provider)
{
var number = _unscaledValue.ToString("G");
if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0)
return number.Insert(number.Length - _scale, ".");
return _unscaledValue.ToString(provider);
}
private string ToStringInt()
{
var number = _unscaledValue.ToString("G");
if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0)
return number.Insert(number.Length - _scale, ".");
StringBuilder buf;
var intString = _unscaledValue.ToString();
var insertionPoint = intString.Length - _scale;
if (insertionPoint == 0)
return (Sign < 0 ? "-0." : "0.") + intString;
if (insertionPoint > 0)
{
buf = new StringBuilder(intString);
buf.Insert(insertionPoint, '.');
if (Sign < 0)
buf.Insert(0, '-');
}
else
{
buf = new StringBuilder(3 - insertionPoint + intString.Length);
buf.Append(Sign < 0 ? "-0." : "0.");
for (var i = 0; i < -insertionPoint; i++)
buf.Append('0');
buf.Append(intString);
}
if (_scale == 0)
buf.Append("0");
return buf.ToString();
}
public static BigDecimal Parse(string value)
{
return new BigDecimal(value);
}
public byte[] ToByteArray()
{
var unscaledValue = _unscaledValue.ToByteArray();
var scale = BitConverter.GetBytes(_scale);
var bytes = new byte[unscaledValue.Length + scale.Length];
Array.Copy(unscaledValue, 0, bytes, 0, unscaledValue.Length);
Array.Copy(scale, 0, bytes, unscaledValue.Length, scale.Length);
return bytes;
}
public (byte[] unscaledValue, byte[] scale) ToByteArrays()
{
return (_unscaledValue.ToByteArray(), BitConverter.GetBytes(_scale));
}
public static BigDecimal Abs(BigDecimal value)
{
return value._unscaledValue.Sign < 0 ? -value : value;
}
public static BigDecimal Negate(BigDecimal value)
{
return new BigDecimal(BigInteger.Negate(value._unscaledValue), value.Scale);
}
public static BigDecimal Add(BigDecimal x, BigDecimal y)
{
return x + y;
}
public static BigDecimal Subtract(BigDecimal x, BigDecimal y)
{
return x - y;
}
public static BigDecimal Multiply(BigDecimal x, BigDecimal y)
{
return x * y;
}
public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor)
{
return dividend / divisor;
}
public static BigDecimal Pow(BigDecimal baseValue, BigInteger exponent)
{
if (exponent.Sign == 0)
return One;
if (exponent.Sign < 0)
{
if (baseValue == Zero)
throw new Exception("Cannot raise zero to a negative power.");
baseValue = One / baseValue;
exponent = BigInteger.Negate(exponent);
}
var result = baseValue;
while (exponent > BigInteger.One)
{
result *= baseValue;
exponent--;
}
return result;
}
public static int Compare(BigDecimal r1, BigDecimal r2)
{
return (r1 - r2)._unscaledValue.Sign;
}
public static bool operator ==(BigDecimal x, BigDecimal y)
{
return x.Equals(y);
}
public static bool operator !=(BigDecimal x, BigDecimal y)
{
return !x.Equals(y);
}
public static bool operator <(BigDecimal x, BigDecimal y)
{
return Compare(x, y) < 0;
}
public static bool operator <=(BigDecimal x, BigDecimal y)
{
return Compare(x, y) <= 0;
}
public static bool operator >(BigDecimal x, BigDecimal y)
{
return Compare(x, y) > 0;
}
public static bool operator >=(BigDecimal x, BigDecimal y)
{
return Compare(x, y) >= 0;
}
public static BigDecimal operator +(BigDecimal value)
{
return value;
}
public static BigDecimal operator -(BigDecimal value)
{
return new BigDecimal(-value._unscaledValue, value.Scale);
}
public static BigDecimal operator ++(BigDecimal value)
{
return value + One;
}
public static BigDecimal operator --(BigDecimal value)
{
return value - One;
}
public static BigDecimal operator +(BigDecimal left, BigDecimal right)
{
BigDecimal ret;
if (left.Scale >= right.Scale)
{
ret = left;
}
else
{
var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
ret = new BigDecimal(value, right.Scale);
}
BigDecimal ret1;
if (right.Scale >= left.Scale)
{
ret1 = right;
}
else
{
var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
ret1 = new BigDecimal(value1, left.Scale);
}
return new BigDecimal(ret._unscaledValue + ret1._unscaledValue, ret.Scale);
}
public static BigDecimal operator -(BigDecimal left, BigDecimal right)
{
BigDecimal ret;
if (left.Scale >= right.Scale)
{
ret = left;
}
else
{
var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
ret = new BigDecimal(value, right.Scale);
}
BigDecimal ret1;
if (right.Scale >= left.Scale)
{
ret1 = right;
}
else
{
var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
ret1 = new BigDecimal(value1, left.Scale);
}
return new BigDecimal(ret._unscaledValue - ret1._unscaledValue, ret.Scale);
}
public static BigDecimal operator *(BigDecimal left, BigDecimal right)
{
return new BigDecimal(left._unscaledValue * right._unscaledValue, left.Scale + right.Scale);
}
public static BigDecimal operator /(BigDecimal left, BigDecimal right)
{
var value = left._unscaledValue;
var scale = left.Scale;
while (scale > 0 && value % 10 == 0)
{
value /= 10;
scale--;
}
var v1 = new BigDecimal(value, scale);
var value1 = right._unscaledValue;
var scale1 = right.Scale;
while (scale1 > 0 && value1 % 10 == 0)
{
value1 /= 10;
scale1--;
}
var v2 = new BigDecimal(value1, scale1);
while (v1.Scale > 0 || v2.Scale > 0)
{
if (v1.Scale > 0)
v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
else
v1 = new BigDecimal(v1._unscaledValue * 10, 0);
if (v2.Scale > 0)
v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
else
v2 = new BigDecimal(v2._unscaledValue * 10, 0);
}
var factor = 0;
var v1Value = v1._unscaledValue;
while (factor < (int)MaxPrecision && v1Value % v2._unscaledValue != 0)
{
v1Value *= 10;
factor++;
}
return new BigDecimal(v1Value / v2._unscaledValue, factor);
}
public static BigDecimal operator %(BigDecimal left, BigDecimal right)
{
var value = left._unscaledValue;
var scale = left.Scale;
while (scale > 0 && value % 10 == 0)
{
value /= 10;
scale--;
}
var v1 = new BigDecimal(value, scale);
var value1 = right._unscaledValue;
var scale1 = right.Scale;
while (scale1 > 0 && value1 % 10 == 0)
{
value1 /= 10;
scale1--;
}
var v2 = new BigDecimal(value1, scale1);
while (v1.Scale > 0 || v2.Scale > 0)
{
if (v1.Scale > 0)
v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
else
v1 = new BigDecimal(v1._unscaledValue * 10, 0);
if (v2.Scale > 0)
v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
else
v2 = new BigDecimal(v2._unscaledValue * 10, 0);
}
return new BigDecimal(v1._unscaledValue % v2._unscaledValue);
}
public static explicit operator sbyte(BigDecimal value)
{
return (sbyte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator ushort(BigDecimal value)
{
return (ushort)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator uint(BigDecimal value)
{
return (uint)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator ulong(BigDecimal value)
{
return (ulong)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator byte(BigDecimal value)
{
return (byte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator short(BigDecimal value)
{
return (short)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator int(BigDecimal value)
{
return (int)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator long(BigDecimal value)
{
return (long)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator BigInteger(BigDecimal value)
{
return BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator xIntX(BigDecimal value)
{
return xIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator BigIntX(BigDecimal value)
{
return BigIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
}
public static explicit operator float(BigDecimal value)
{
return (float)(double)value;
}
public static explicit operator double(BigDecimal value)
{
var factor = BigInteger.Pow(10, value.Scale);
if (SafeCastToDouble(value._unscaledValue) && SafeCastToDouble(factor))
return (double)value._unscaledValue / (double)factor;
var dnv = value._unscaledValue * DoublePrecision / factor;
if (dnv.IsZero)
return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d;
double result = 0;
var isDouble = false;
var scale = 308;
while (scale > 0)
{
if (!isDouble)
{
if (SafeCastToDouble(dnv))
{
result = (double)dnv;
isDouble = true;
}
else
{
dnv /= 10;
}
}
result /= 10;
scale--;
}
if (!isDouble)
return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity;
return result;
}
public static explicit operator decimal(BigDecimal value)
{
var factor = BigInteger.Pow(10, value.Scale);
if (SafeCastToDecimal(value._unscaledValue) && SafeCastToDecimal(factor))
return (decimal)value._unscaledValue / (decimal)factor;
var dnv = value._unscaledValue * DecimalPrecision / factor;
if (dnv.IsZero)
return decimal.Zero;
for (var scale = 28; scale >= 0; scale--)
if (!SafeCastToDecimal(dnv))
{
dnv /= 10;
}
else
{
var dec = new DecimalUInt32();
dec.dec = (decimal)dnv;
dec.flags = (dec.flags & ~0x00FF0000) | (scale << 16);
return dec.dec;
}
throw new Exception("Value was either too large or too small for a Decimal.");
}
public static implicit operator BigDecimal(sbyte value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(ushort value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(uint value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(ulong value)
{
return new BigDecimal((BigInteger)value);
}
public static implicit operator BigDecimal(byte value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(short value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(int value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(long value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(BigInteger value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(xIntX value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(BigIntX value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(float value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(double value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(decimal value)
{
return new BigDecimal(value);
}
public static implicit operator BigDecimal(BigRational value)
{
return new BigDecimal(value);
}
private static bool SafeCastToDouble(BigInteger value)
{
return DoubleMinValue <= value && value <= DoubleMaxValue;
}
private static bool SafeCastToDecimal(BigInteger value)
{
return DecimalMinValue <= value && value <= DecimalMaxValue;
}
private static BigInteger GetLastDigit(BigInteger value)
{
return value % new BigInteger(10);
}
private static int GetNumberOfDigits(BigInteger value)
{
return BigInteger.Abs(value).ToString().Length;
}
private static BigDecimal Factorial(BigDecimal x)
{
BigDecimal r = 1;
BigDecimal c = 1;
while (c <= x)
{
r *= c;
c++;
}
return r;
}
public static BigDecimal Exp(BigDecimal x)
{
BigDecimal r = 0;
BigDecimal r1 = 0;
var k = 0;
while (true)
{
r += Pow(x, k) / Factorial(k);
if (r == r1)
break;
r1 = r;
k++;
}
return r;
}
public static BigDecimal Sine(BigDecimal ar, int n)
{
if (Factorials == null)
{
Factorials = new BigDecimal[MaxFactorials];
for (var i = 0; i < MaxFactorials; i++)
Factorials[i] = new BigDecimal(0, 0);
for (var i = 1; i < MaxFactorials + 1; i++)
Factorials[i - 1] = Factorial(i);
}
var sin = ar;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2 + 1);
trm /= Factorials[i * 2];
if ((i & 1) == 1)
sin -= trm;
else
sin += trm;
}
return sin;
}
public static BigDecimal Atan(BigDecimal ar, int n)
{
var atan = ar;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2 + 1);
trm /= i * 2;
if ((i & 1) == 1)
atan -= trm;
else
atan += trm;
}
return atan;
}
public static BigDecimal Cosine(BigDecimal ar, int n)
{
if (Factorials == null)
{
Factorials = new BigDecimal[MaxFactorials];
for (var i = 0; i < MaxFactorials; i++)
Factorials[i] = new BigDecimal(0, 0);
for (var i = 1; i < MaxFactorials + 1; i++)
Factorials[i - 1] = Factorial(i);
}
BigDecimal cos = 1.0;
for (var i = 1; i <= n; i++)
{
var trm = Pow(ar, i * 2);
trm /= Factorials[i * 2 - 1];
if ((i & 1) == 1)
cos -= trm;
else
cos += trm;
}
return cos;
}
public static BigDecimal GetE(int n)
{
BigDecimal e = 1.0;
var c = n;
while (c > 0)
{
BigDecimal f = 0;
if (c == 1)
{
f = 1;
}
else
{
var i = c - 1;
f = c;
while (i > 0)
{
f *= i;
i--;
}
}
c--;
e += 1.0 / f;
}
return e;
}
public static BigDecimal Tangent(BigDecimal ar, int n)
{
return Sine(ar, n) / Cosine(ar, n);
}
public static BigDecimal CoTangent(BigDecimal ar, int n)
{
return Cosine(ar, n) / Sine(ar, n);
}
public static BigDecimal Secant(BigDecimal ar, int n)
{
return 1.0 / Cosine(ar, n);
}
public static BigDecimal NthRoot(BigDecimal value, int nth)
{
BigDecimal lx;
var a = value;
var n = nth;
BigDecimal s = 1.0;
do
{
var t = s;
lx = a / Pow(s, n - 1);
var r = (n - 1) * s;
s = (lx + r) / n;
} while (lx != s);
return s;
}
public static BigDecimal LogN(BigDecimal value)
{
var E = GetE(MaxFactorials);
BigDecimal a;
var p = value;
BigDecimal n = 0.0;
while (p >= E)
{
p /= E;
n++;
}
n += p / E;
p = value;
do
{
a = n;
var lx = p / Exp(n - 1.0);
var r = (n - 1.0) * E;
n = (lx + r) / E;
} while (n != a);
return n;
}
public static BigDecimal Log(BigDecimal n, int b)
{
return LogN(n) / LogN(b);
}
public static BigDecimal CoSecant(BigDecimal ar, int n)
{
return 1.0 / Sine(ar, n);
}
public static BigDecimal Ceiling(BigDecimal value, int precision)
{
var v1 = new BigDecimal(value);
v1 = RemoveTrailingZeros(v1);
var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
if (diff > 0)
{
for (var i = 0; i < diff; i++)
{
v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
v1.Scale--;
}
if (v1._unscaledValue.Sign < 0)
v1._unscaledValue--;
else
v1._unscaledValue++;
}
return v1;
}
public static BigDecimal Floor(BigDecimal value, int precision)
{
var v1 = new BigDecimal(value);
v1 = RemoveTrailingZeros(v1);
var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
if (diff > 0)
{
for (var i = 0; i < diff; i++)
{
v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
v1.Scale--;
}
if (v1._unscaledValue.Sign > 0)
v1._unscaledValue--;
else
v1._unscaledValue++;
}
return v1;
}
private static BigDecimal RemoveTrailingZeros(BigDecimal value)
{
var v1 = new BigDecimal(value);
BigInteger remainder;
do
{
var shortened = BigInteger.DivRem(v1._unscaledValue, 10, out remainder);
if (remainder == BigInteger.Zero)
{
v1._unscaledValue = shortened;
v1.Scale--;
}
} while (remainder == BigInteger.Zero);
return v1;
}
public BigDecimal Min(BigDecimal value)
{
return CompareTo(value) <= 0 ? this : value;
}
public BigDecimal Max(BigDecimal value)
{
return CompareTo(value) >= 0 ? this : value;
}
public static BigDecimal Sqrt(BigDecimal value)
{
return BigRational.Sqrt(value);
}
[StructLayout(LayoutKind.Explicit)]
internal struct DecimalUInt32
{
[FieldOffset(0)] public decimal dec;
[FieldOffset(0)] public int flags;
}
}
using System; using System.Diagnostics; using System.Numerics; using System.Runtime.InteropServices; using System.Text; [Serializable] [DebuggerDisplay("{DDisplay}")] public struct BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal> { private const int MaxFactorials = 200; private static int _bitWidth = 2048; public static float MaxPrecision = _bitWidth / 64f * 20f; private static readonly BigInteger DoublePrecision = BigInteger.Pow(10, 308); private static readonly BigInteger DoubleMaxValue = (BigInteger)double.MaxValue; private static readonly BigInteger DoubleMinValue = (BigInteger)double.MinValue; private static readonly BigInteger DecimalPrecision = BigInteger.Pow(10, 28); private static readonly BigInteger DecimalMaxValue = (BigInteger)decimal.MaxValue; private static readonly BigInteger DecimalMinValue = (BigInteger)decimal.MinValue; private static BigDecimal[] Factorials; private int _scale; private BigInteger _unscaledValue; public BigDecimal(BigInteger value) : this(value, 0) { } public BigDecimal(xIntX value) : this((BigInteger)value, 0) { } public BigDecimal(BigIntX value) : this((BigInteger)value, 0) { } public BigDecimal(BigInteger value, int scale) { _unscaledValue = value; _scale = scale; } public BigDecimal(long value) : this(value, 0) { } public BigDecimal(double value) : this((decimal)value) { } public BigDecimal(float value) : this((decimal)value) { } public BigDecimal(byte[] value) { var number = new byte[value.Length - 4]; var flags = new byte[4]; Array.Copy(value, 0, number, 0, number.Length); Array.Copy(value, value.Length - 4, flags, 0, 4); _unscaledValue = new BigInteger(number); _scale = BitConverter.ToInt32(flags, 0); } public BigDecimal(BigRational value) { var num = (BigDecimal)value.Numerator; var den = (BigDecimal)value.Denominator; var bigDecRes = num / den; _unscaledValue = bigDecRes._unscaledValue; _scale = bigDecRes.Scale; } public BigDecimal(BigDecimal value) { _unscaledValue = value._unscaledValue; _scale = value.Scale; } public BigDecimal(decimal value) { var bytes = new byte[16]; var bits = decimal.GetBits(value); var lo = bits[0]; var mid = bits[1]; var hi = bits[2]; var flags = bits[3]; bytes[0] = (byte)lo; bytes[1] = (byte)(lo >> 8); bytes[2] = (byte)(lo >> 0x10); bytes[3] = (byte)(lo >> 0x18); bytes[4] = (byte)mid; bytes[5] = (byte)(mid >> 8); bytes[6] = (byte)(mid >> 0x10); bytes[7] = (byte)(mid >> 0x18); bytes[8] = (byte)hi; bytes[9] = (byte)(hi >> 8); bytes[10] = (byte)(hi >> 0x10); bytes[11] = (byte)(hi >> 0x18); bytes[12] = (byte)flags; bytes[13] = (byte)(flags >> 8); bytes[14] = (byte)(flags >> 0x10); bytes[15] = (byte)(flags >> 0x18); var unscaledValueBytes = new byte[12]; Array.Copy(bytes, unscaledValueBytes, unscaledValueBytes.Length); var unscaledValue = new BigInteger(unscaledValueBytes); var scale = bytes[14]; if (bytes[15] == 128) unscaledValue *= BigInteger.MinusOne; _unscaledValue = unscaledValue; _scale = scale; } public BigDecimal(string value) { if (!value.ContainsOnly("0123456789+-.eE")) throw new Exception($"Input value must only contain these '0123456789+-.eE', value'{value}"); var len = value.Length; var start = 0; var point = 0; var dot = -1; var negative = false; if (value[0] == '+') { ++start; ++point; } else if (value[0] == '-') { ++start; ++point; negative = true; } while (point < len) { var c = value[point]; if (c == '.') { if (dot >= 0) throw new Exception($"There are multiple '.'s in the value {value}"); dot = point; } else if (c == 'e' || c == 'E') { break; } ++point; } string val; if (dot >= 0) { val = value.Substring(start, dot) + value.Substring(dot + 1, point - (dot + 1)); _scale = point - 1 - dot; } else { val = value.Substring(start, point); _scale = 0; } if (val.Length == 0) throw new Exception($"There are no digits in the value {value}"); if (negative) val = "-" + val; _unscaledValue = val.BigIntegerBase10(); if (point < len) try { point++; switch (value[point]) { case '+': { point++; if (point >= len) throw new Exception($"No exponent following e or E. Value {value}"); var scale = value.Substring(point); _scale -= int.Parse(scale); return; } case '-': { point++; if (point >= len) throw new Exception($"No exponent following e or E. Value {value}"); var scale = value.Substring(point); _scale += int.Parse(scale); return; } default: throw new Exception($"Malformed exponent in value {value}"); } } catch (Exception ex) { throw new Exception($"Malformed exponent in value {value}"); } } public int BitWidth { get => _bitWidth; set { _bitWidth = value; MaxPrecision = _bitWidth / 64f * 20f; } } private string DDisplay => ToString(); public static BigDecimal Zero { get; } = new(BigInteger.Zero); public static BigDecimal One { get; } = new(BigInteger.One); public static BigDecimal MinusOne { get; } = new(BigInteger.MinusOne); public bool IsEven => _unscaledValue.IsEven; public bool IsOne => _unscaledValue.IsOne; public bool IsPowerOfTwo => _unscaledValue.IsPowerOfTwo; public bool IsZero => _unscaledValue.IsZero; public int Sign => _unscaledValue.Sign; public int Scale { get => _scale; private set => _scale = value; } public BigInteger UnscaledValue => _unscaledValue; public BigDecimal WholePart => BigInteger.Divide(_unscaledValue, BigInteger.Pow(10, Scale)); public BigDecimal FractionalPart => this - WholePart; public int DecimalPlaces { get { var a = new BigDecimal(_unscaledValue); var dPlaces = 0; if (a.Sign == 0) return 1; if (a.Sign < 0) try { a = -a; } catch (Exception ex) { return 0; } var biRadix = new BigDecimal(10); while (a > 0) try { a /= biRadix; dPlaces++; } catch (Exception ex) { break; } return dPlaces; } } int IComparable.CompareTo(object obj) { if (obj == null) return 1; if (!(obj is BigDecimal)) throw new Exception("Argument must be of type BigDecimal."); return Compare(this, (BigDecimal)obj); } public int CompareTo(BigDecimal other) { return Compare(this, other); } public bool Equals(BigDecimal other) { return _unscaledValue == other._unscaledValue && _scale == other.Scale; } public override bool Equals(object obj) { if (obj == null) return false; if (!(obj is BigDecimal)) return false; return Equals((BigDecimal)obj); } public static BigDecimal Round(BigDecimal number, int decimalPlaces) { BigDecimal power = BigInteger.Pow(10, decimalPlaces); number *= power; return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; } public void Round(int decimalPlaces) { var number = this; BigDecimal power = BigInteger.Pow(10, decimalPlaces); number *= power; var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power; _unscaledValue = n._unscaledValue; _scale = n.Scale; } public override int GetHashCode() { return UnscaledValue.GetHashCode() ^ _scale.GetHashCode(); } public override string ToString() { return ToStringInt(); } public string ToString(IFormatProvider provider) { var number = _unscaledValue.ToString("G"); if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0) return number.Insert(number.Length - _scale, "."); return _unscaledValue.ToString(provider); } private string ToStringInt() { var number = _unscaledValue.ToString("G"); if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0) return number.Insert(number.Length - _scale, "."); StringBuilder buf; var intString = _unscaledValue.ToString(); var insertionPoint = intString.Length - _scale; if (insertionPoint == 0) return (Sign < 0 ? "-0." : "0.") + intString; if (insertionPoint > 0) { buf = new StringBuilder(intString); buf.Insert(insertionPoint, '.'); if (Sign < 0) buf.Insert(0, '-'); } else { buf = new StringBuilder(3 - insertionPoint + intString.Length); buf.Append(Sign < 0 ? "-0." : "0."); for (var i = 0; i < -insertionPoint; i++) buf.Append('0'); buf.Append(intString); } if (_scale == 0) buf.Append("0"); return buf.ToString(); } public static BigDecimal Parse(string value) { return new BigDecimal(value); } public byte[] ToByteArray() { var unscaledValue = _unscaledValue.ToByteArray(); var scale = BitConverter.GetBytes(_scale); var bytes = new byte[unscaledValue.Length + scale.Length]; Array.Copy(unscaledValue, 0, bytes, 0, unscaledValue.Length); Array.Copy(scale, 0, bytes, unscaledValue.Length, scale.Length); return bytes; } public (byte[] unscaledValue, byte[] scale) ToByteArrays() { return (_unscaledValue.ToByteArray(), BitConverter.GetBytes(_scale)); } public static BigDecimal Abs(BigDecimal value) { return value._unscaledValue.Sign < 0 ? -value : value; } public static BigDecimal Negate(BigDecimal value) { return new BigDecimal(BigInteger.Negate(value._unscaledValue), value.Scale); } public static BigDecimal Add(BigDecimal x, BigDecimal y) { return x + y; } public static BigDecimal Subtract(BigDecimal x, BigDecimal y) { return x - y; } public static BigDecimal Multiply(BigDecimal x, BigDecimal y) { return x * y; } public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor) { return dividend / divisor; } public static BigDecimal Pow(BigDecimal baseValue, BigInteger exponent) { if (exponent.Sign == 0) return One; if (exponent.Sign < 0) { if (baseValue == Zero) throw new Exception("Cannot raise zero to a negative power."); baseValue = One / baseValue; exponent = BigInteger.Negate(exponent); } var result = baseValue; while (exponent > BigInteger.One) { result *= baseValue; exponent--; } return result; } public static int Compare(BigDecimal r1, BigDecimal r2) { return (r1 - r2)._unscaledValue.Sign; } public static bool operator ==(BigDecimal x, BigDecimal y) { return x.Equals(y); } public static bool operator !=(BigDecimal x, BigDecimal y) { return !x.Equals(y); } public static bool operator <(BigDecimal x, BigDecimal y) { return Compare(x, y) < 0; } public static bool operator <=(BigDecimal x, BigDecimal y) { return Compare(x, y) <= 0; } public static bool operator >(BigDecimal x, BigDecimal y) { return Compare(x, y) > 0; } public static bool operator >=(BigDecimal x, BigDecimal y) { return Compare(x, y) >= 0; } public static BigDecimal operator +(BigDecimal value) { return value; } public static BigDecimal operator -(BigDecimal value) { return new BigDecimal(-value._unscaledValue, value.Scale); } public static BigDecimal operator ++(BigDecimal value) { return value + One; } public static BigDecimal operator --(BigDecimal value) { return value - One; } public static BigDecimal operator +(BigDecimal left, BigDecimal right) { BigDecimal ret; if (left.Scale >= right.Scale) { ret = left; } else { var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale); ret = new BigDecimal(value, right.Scale); } BigDecimal ret1; if (right.Scale >= left.Scale) { ret1 = right; } else { var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale); ret1 = new BigDecimal(value1, left.Scale); } return new BigDecimal(ret._unscaledValue + ret1._unscaledValue, ret.Scale); } public static BigDecimal operator -(BigDecimal left, BigDecimal right) { BigDecimal ret; if (left.Scale >= right.Scale) { ret = left; } else { var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale); ret = new BigDecimal(value, right.Scale); } BigDecimal ret1; if (right.Scale >= left.Scale) { ret1 = right; } else { var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale); ret1 = new BigDecimal(value1, left.Scale); } return new BigDecimal(ret._unscaledValue - ret1._unscaledValue, ret.Scale); } public static BigDecimal operator *(BigDecimal left, BigDecimal right) { return new BigDecimal(left._unscaledValue * right._unscaledValue, left.Scale + right.Scale); } public static BigDecimal operator /(BigDecimal left, BigDecimal right) { var value = left._unscaledValue; var scale = left.Scale; while (scale > 0 && value % 10 == 0) { value /= 10; scale--; } var v1 = new BigDecimal(value, scale); var value1 = right._unscaledValue; var scale1 = right.Scale; while (scale1 > 0 && value1 % 10 == 0) { value1 /= 10; scale1--; } var v2 = new BigDecimal(value1, scale1); while (v1.Scale > 0 || v2.Scale > 0) { if (v1.Scale > 0) v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1); else v1 = new BigDecimal(v1._unscaledValue * 10, 0); if (v2.Scale > 0) v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1); else v2 = new BigDecimal(v2._unscaledValue * 10, 0); } var factor = 0; var v1Value = v1._unscaledValue; while (factor < (int)MaxPrecision && v1Value % v2._unscaledValue != 0) { v1Value *= 10; factor++; } return new BigDecimal(v1Value / v2._unscaledValue, factor); } public static BigDecimal operator %(BigDecimal left, BigDecimal right) { var value = left._unscaledValue; var scale = left.Scale; while (scale > 0 && value % 10 == 0) { value /= 10; scale--; } var v1 = new BigDecimal(value, scale); var value1 = right._unscaledValue; var scale1 = right.Scale; while (scale1 > 0 && value1 % 10 == 0) { value1 /= 10; scale1--; } var v2 = new BigDecimal(value1, scale1); while (v1.Scale > 0 || v2.Scale > 0) { if (v1.Scale > 0) v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1); else v1 = new BigDecimal(v1._unscaledValue * 10, 0); if (v2.Scale > 0) v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1); else v2 = new BigDecimal(v2._unscaledValue * 10, 0); } return new BigDecimal(v1._unscaledValue % v2._unscaledValue); } public static explicit operator sbyte(BigDecimal value) { return (sbyte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator ushort(BigDecimal value) { return (ushort)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator uint(BigDecimal value) { return (uint)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator ulong(BigDecimal value) { return (ulong)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator byte(BigDecimal value) { return (byte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator short(BigDecimal value) { return (short)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator int(BigDecimal value) { return (int)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator long(BigDecimal value) { return (long)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator BigInteger(BigDecimal value) { return BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator xIntX(BigDecimal value) { return xIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator BigIntX(BigDecimal value) { return BigIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale)); } public static explicit operator float(BigDecimal value) { return (float)(double)value; } public static explicit operator double(BigDecimal value) { var factor = BigInteger.Pow(10, value.Scale); if (SafeCastToDouble(value._unscaledValue) && SafeCastToDouble(factor)) return (double)value._unscaledValue / (double)factor; var dnv = value._unscaledValue * DoublePrecision / factor; if (dnv.IsZero) return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d; double result = 0; var isDouble = false; var scale = 308; while (scale > 0) { if (!isDouble) { if (SafeCastToDouble(dnv)) { result = (double)dnv; isDouble = true; } else { dnv /= 10; } } result /= 10; scale--; } if (!isDouble) return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity; return result; } public static explicit operator decimal(BigDecimal value) { var factor = BigInteger.Pow(10, value.Scale); if (SafeCastToDecimal(value._unscaledValue) && SafeCastToDecimal(factor)) return (decimal)value._unscaledValue / (decimal)factor; var dnv = value._unscaledValue * DecimalPrecision / factor; if (dnv.IsZero) return decimal.Zero; for (var scale = 28; scale >= 0; scale--) if (!SafeCastToDecimal(dnv)) { dnv /= 10; } else { var dec = new DecimalUInt32(); dec.dec = (decimal)dnv; dec.flags = (dec.flags & ~0x00FF0000) | (scale << 16); return dec.dec; } throw new Exception("Value was either too large or too small for a Decimal."); } public static implicit operator BigDecimal(sbyte value) { return new BigDecimal(value); } public static implicit operator BigDecimal(ushort value) { return new BigDecimal(value); } public static implicit operator BigDecimal(uint value) { return new BigDecimal(value); } public static implicit operator BigDecimal(ulong value) { return new BigDecimal((BigInteger)value); } public static implicit operator BigDecimal(byte value) { return new BigDecimal(value); } public static implicit operator BigDecimal(short value) { return new BigDecimal(value); } public static implicit operator BigDecimal(int value) { return new BigDecimal(value); } public static implicit operator BigDecimal(long value) { return new BigDecimal(value); } public static implicit operator BigDecimal(BigInteger value) { return new BigDecimal(value); } public static implicit operator BigDecimal(xIntX value) { return new BigDecimal(value); } public static implicit operator BigDecimal(BigIntX value) { return new BigDecimal(value); } public static implicit operator BigDecimal(float value) { return new BigDecimal(value); } public static implicit operator BigDecimal(double value) { return new BigDecimal(value); } public static implicit operator BigDecimal(decimal value) { return new BigDecimal(value); } public static implicit operator BigDecimal(BigRational value) { return new BigDecimal(value); } private static bool SafeCastToDouble(BigInteger value) { return DoubleMinValue <= value && value <= DoubleMaxValue; } private static bool SafeCastToDecimal(BigInteger value) { return DecimalMinValue <= value && value <= DecimalMaxValue; } private static BigInteger GetLastDigit(BigInteger value) { return value % new BigInteger(10); } private static int GetNumberOfDigits(BigInteger value) { return BigInteger.Abs(value).ToString().Length; } private static BigDecimal Factorial(BigDecimal x) { BigDecimal r = 1; BigDecimal c = 1; while (c <= x) { r *= c; c++; } return r; } public static BigDecimal Exp(BigDecimal x) { BigDecimal r = 0; BigDecimal r1 = 0; var k = 0; while (true) { r += Pow(x, k) / Factorial(k); if (r == r1) break; r1 = r; k++; } return r; } public static BigDecimal Sine(BigDecimal ar, int n) { if (Factorials == null) { Factorials = new BigDecimal[MaxFactorials]; for (var i = 0; i < MaxFactorials; i++) Factorials[i] = new BigDecimal(0, 0); for (var i = 1; i < MaxFactorials + 1; i++) Factorials[i - 1] = Factorial(i); } var sin = ar; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2 + 1); trm /= Factorials[i * 2]; if ((i & 1) == 1) sin -= trm; else sin += trm; } return sin; } public static BigDecimal Atan(BigDecimal ar, int n) { var atan = ar; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2 + 1); trm /= i * 2; if ((i & 1) == 1) atan -= trm; else atan += trm; } return atan; } public static BigDecimal Cosine(BigDecimal ar, int n) { if (Factorials == null) { Factorials = new BigDecimal[MaxFactorials]; for (var i = 0; i < MaxFactorials; i++) Factorials[i] = new BigDecimal(0, 0); for (var i = 1; i < MaxFactorials + 1; i++) Factorials[i - 1] = Factorial(i); } BigDecimal cos = 1.0; for (var i = 1; i <= n; i++) { var trm = Pow(ar, i * 2); trm /= Factorials[i * 2 - 1]; if ((i & 1) == 1) cos -= trm; else cos += trm; } return cos; } public static BigDecimal GetE(int n) { BigDecimal e = 1.0; var c = n; while (c > 0) { BigDecimal f = 0; if (c == 1) { f = 1; } else { var i = c - 1; f = c; while (i > 0) { f *= i; i--; } } c--; e += 1.0 / f; } return e; } public static BigDecimal Tangent(BigDecimal ar, int n) { return Sine(ar, n) / Cosine(ar, n); } public static BigDecimal CoTangent(BigDecimal ar, int n) { return Cosine(ar, n) / Sine(ar, n); } public static BigDecimal Secant(BigDecimal ar, int n) { return 1.0 / Cosine(ar, n); } public static BigDecimal NthRoot(BigDecimal value, int nth) { BigDecimal lx; var a = value; var n = nth; BigDecimal s = 1.0; do { var t = s; lx = a / Pow(s, n - 1); var r = (n - 1) * s; s = (lx + r) / n; } while (lx != s); return s; } public static BigDecimal LogN(BigDecimal value) { var E = GetE(MaxFactorials); BigDecimal a; var p = value; BigDecimal n = 0.0; while (p >= E) { p /= E; n++; } n += p / E; p = value; do { a = n; var lx = p / Exp(n - 1.0); var r = (n - 1.0) * E; n = (lx + r) / E; } while (n != a); return n; } public static BigDecimal Log(BigDecimal n, int b) { return LogN(n) / LogN(b); } public static BigDecimal CoSecant(BigDecimal ar, int n) { return 1.0 / Sine(ar, n); } public static BigDecimal Ceiling(BigDecimal value, int precision) { var v1 = new BigDecimal(value); v1 = RemoveTrailingZeros(v1); var diff = GetNumberOfDigits(v1._unscaledValue) - precision; if (diff > 0) { for (var i = 0; i < diff; i++) { v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10); v1.Scale--; } if (v1._unscaledValue.Sign < 0) v1._unscaledValue--; else v1._unscaledValue++; } return v1; } public static BigDecimal Floor(BigDecimal value, int precision) { var v1 = new BigDecimal(value); v1 = RemoveTrailingZeros(v1); var diff = GetNumberOfDigits(v1._unscaledValue) - precision; if (diff > 0) { for (var i = 0; i < diff; i++) { v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10); v1.Scale--; } if (v1._unscaledValue.Sign > 0) v1._unscaledValue--; else v1._unscaledValue++; } return v1; } private static BigDecimal RemoveTrailingZeros(BigDecimal value) { var v1 = new BigDecimal(value); BigInteger remainder; do { var shortened = BigInteger.DivRem(v1._unscaledValue, 10, out remainder); if (remainder == BigInteger.Zero) { v1._unscaledValue = shortened; v1.Scale--; } } while (remainder == BigInteger.Zero); return v1; } public BigDecimal Min(BigDecimal value) { return CompareTo(value) <= 0 ? this : value; } public BigDecimal Max(BigDecimal value) { return CompareTo(value) >= 0 ? this : value; } public static BigDecimal Sqrt(BigDecimal value) { return BigRational.Sqrt(value); } [StructLayout(LayoutKind.Explicit)] internal struct DecimalUInt32 { [FieldOffset(0)] public decimal dec; [FieldOffset(0)] public int flags; } }
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[DebuggerDisplay("{DDisplay}")]
public struct BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal>
{
    private const           int          MaxFactorials    = 200;
    private static          int          _bitWidth        = 2048;
    public static           float        MaxPrecision     = _bitWidth / 64f * 20f;
    private static readonly BigInteger   DoublePrecision  = BigInteger.Pow(10, 308);
    private static readonly BigInteger   DoubleMaxValue   = (BigInteger)double.MaxValue;
    private static readonly BigInteger   DoubleMinValue   = (BigInteger)double.MinValue;
    private static readonly BigInteger   DecimalPrecision = BigInteger.Pow(10, 28);
    private static readonly BigInteger   DecimalMaxValue  = (BigInteger)decimal.MaxValue;
    private static readonly BigInteger   DecimalMinValue  = (BigInteger)decimal.MinValue;
    private static          BigDecimal[] Factorials;
    private                 int          _scale;
    private                 BigInteger   _unscaledValue;
    public BigDecimal(BigInteger value) : this(value, 0)
    {
    }
    public BigDecimal(xIntX value) : this((BigInteger)value, 0)
    {
    }
    public BigDecimal(BigIntX value) : this((BigInteger)value, 0)
    {
    }
    public BigDecimal(BigInteger value, int scale)
    {
        _unscaledValue = value;
        _scale         = scale;
    }
    public BigDecimal(long value) : this(value, 0)
    {
    }
    public BigDecimal(double value) : this((decimal)value)
    {
    }
    public BigDecimal(float value) : this((decimal)value)
    {
    }
    public BigDecimal(byte[] value)
    {
        var number = new byte[value.Length - 4];
        var flags  = new byte[4];
        Array.Copy(value, 0,                number, 0, number.Length);
        Array.Copy(value, value.Length - 4, flags,  0, 4);
        _unscaledValue = new BigInteger(number);
        _scale         = BitConverter.ToInt32(flags, 0);
    }
    public BigDecimal(BigRational value)
    {
        var num       = (BigDecimal)value.Numerator;
        var den       = (BigDecimal)value.Denominator;
        var bigDecRes = num / den;
        _unscaledValue = bigDecRes._unscaledValue;
        _scale         = bigDecRes.Scale;
    }
    public BigDecimal(BigDecimal value)
    {
        _unscaledValue = value._unscaledValue;
        _scale         = value.Scale;
    }
    public BigDecimal(decimal value)
    {
        var bytes = new byte[16];
        var bits  = decimal.GetBits(value);
        var lo    = bits[0];
        var mid   = bits[1];
        var hi    = bits[2];
        var flags = bits[3];
        bytes[0]  = (byte)lo;
        bytes[1]  = (byte)(lo >> 8);
        bytes[2]  = (byte)(lo >> 0x10);
        bytes[3]  = (byte)(lo >> 0x18);
        bytes[4]  = (byte)mid;
        bytes[5]  = (byte)(mid >> 8);
        bytes[6]  = (byte)(mid >> 0x10);
        bytes[7]  = (byte)(mid >> 0x18);
        bytes[8]  = (byte)hi;
        bytes[9]  = (byte)(hi >> 8);
        bytes[10] = (byte)(hi >> 0x10);
        bytes[11] = (byte)(hi >> 0x18);
        bytes[12] = (byte)flags;
        bytes[13] = (byte)(flags >> 8);
        bytes[14] = (byte)(flags >> 0x10);
        bytes[15] = (byte)(flags >> 0x18);
        var unscaledValueBytes = new byte[12];
        Array.Copy(bytes, unscaledValueBytes, unscaledValueBytes.Length);
        var unscaledValue = new BigInteger(unscaledValueBytes);
        var scale         = bytes[14];
        if (bytes[15] == 128)
            unscaledValue *= BigInteger.MinusOne;
        _unscaledValue = unscaledValue;
        _scale         = scale;
    }
    public BigDecimal(string value)
    {
        if (!value.ContainsOnly("0123456789+-.eE"))
            throw new Exception($"Input value must only contain these '0123456789+-.eE', value'{value}");
        var len      = value.Length;
        var start    = 0;
        var point    = 0;
        var dot      = -1;
        var negative = false;
        if (value[0] == '+')
        {
            ++start;
            ++point;
        }
        else if (value[0] == '-')
        {
            ++start;
            ++point;
            negative = true;
        }
        while (point < len)
        {
            var c = value[point];
            if (c == '.')
            {
                if (dot >= 0)
                    throw new Exception($"There are multiple '.'s in the value {value}");
                dot = point;
            }
            else if (c == 'e' || c == 'E')
            {
                break;
            }
            ++point;
        }
        string val;
        if (dot >= 0)
        {
            val    = value.Substring(start, dot) + value.Substring(dot + 1, point - (dot + 1));
            _scale = point                       - 1 - dot;
        }
        else
        {
            val    = value.Substring(start, point);
            _scale = 0;
        }
        if (val.Length == 0)
            throw new Exception($"There are no digits in the value {value}");
        if (negative)
            val = "-" + val;
        _unscaledValue = val.BigIntegerBase10();
        if (point < len)
            try
            {
                point++;
                switch (value[point])
                {
                    case '+':
                    {
                        point++;
                        if (point >= len)
                            throw new Exception($"No exponent following e or E. Value {value}");
                        var scale = value.Substring(point);
                        _scale -= int.Parse(scale);
                        return;
                    }
                    case '-':
                    {
                        point++;
                        if (point >= len)
                            throw new Exception($"No exponent following e or E. Value {value}");
                        var scale = value.Substring(point);
                        _scale += int.Parse(scale);
                        return;
                    }
                    default:
                        throw new Exception($"Malformed exponent in value {value}");
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"Malformed exponent in value {value}");
            }
    }
    public int BitWidth
    {
        get => _bitWidth;
        set
        {
            _bitWidth    = value;
            MaxPrecision = _bitWidth / 64f * 20f;
        }
    }
    private string DDisplay => ToString();
    public static BigDecimal Zero
    {
        get;
    } = new(BigInteger.Zero);
    public static BigDecimal One
    {
        get;
    } = new(BigInteger.One);
    public static BigDecimal MinusOne
    {
        get;
    } = new(BigInteger.MinusOne);
    public bool IsEven       => _unscaledValue.IsEven;
    public bool IsOne        => _unscaledValue.IsOne;
    public bool IsPowerOfTwo => _unscaledValue.IsPowerOfTwo;
    public bool IsZero       => _unscaledValue.IsZero;
    public int  Sign         => _unscaledValue.Sign;
    public int Scale
    {
        get => _scale;
        private set => _scale = value;
    }
    public BigInteger UnscaledValue  => _unscaledValue;
    public BigDecimal WholePart      => BigInteger.Divide(_unscaledValue, BigInteger.Pow(10, Scale));
    public BigDecimal FractionalPart => this - WholePart;
    public int DecimalPlaces
    {
        get
        {
            var a       = new BigDecimal(_unscaledValue);
            var dPlaces = 0;
            if (a.Sign == 0)
                return 1;
            if (a.Sign < 0)
                try
                {
                    a = -a;
                }
                catch (Exception ex)
                {
                    return 0;
                }
            var biRadix = new BigDecimal(10);
            while (a > 0)
                try
                {
                    a /= biRadix;
                    dPlaces++;
                }
                catch (Exception ex)
                {
                    break;
                }
            return dPlaces;
        }
    }
    int IComparable.CompareTo(object obj)
    {
        if (obj == null)
            return 1;
        if (!(obj is BigDecimal))
            throw new Exception("Argument must be of type BigDecimal.");
        return Compare(this, (BigDecimal)obj);
    }
    public int CompareTo(BigDecimal other)
    {
        return Compare(this, other);
    }
    public bool Equals(BigDecimal other)
    {
        return _unscaledValue == other._unscaledValue && _scale == other.Scale;
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (!(obj is BigDecimal))
            return false;
        return Equals((BigDecimal)obj);
    }
    public static BigDecimal Round(BigDecimal number, int decimalPlaces)
    {
        BigDecimal power = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        return number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
    }
    public void Round(int decimalPlaces)
    {
        var        number = this;
        BigDecimal power  = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        var n = number >= 0 ? (BigInteger)(number + 0.5) / power : (BigInteger)(number - 0.5) / power;
        _unscaledValue = n._unscaledValue;
        _scale         = n.Scale;
    }
    public override int GetHashCode()
    {
        return UnscaledValue.GetHashCode() ^ _scale.GetHashCode();
    }
    public override string ToString()
    {
        return ToStringInt();
    }
    public string ToString(IFormatProvider provider)
    {
        var number = _unscaledValue.ToString("G");
        if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0)
            return number.Insert(number.Length - _scale, ".");
        return _unscaledValue.ToString(provider);
    }
    private string ToStringInt()
    {
        var number = _unscaledValue.ToString("G");
        if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0)
            return number.Insert(number.Length - _scale, ".");
        StringBuilder buf;
        var           intString      = _unscaledValue.ToString();
        var           insertionPoint = intString.Length - _scale;
        if (insertionPoint == 0)
            return (Sign < 0 ? "-0." : "0.") + intString;
        if (insertionPoint > 0)
        {
            buf = new StringBuilder(intString);
            buf.Insert(insertionPoint, '.');
            if (Sign < 0)
                buf.Insert(0, '-');
        }
        else
        {
            buf = new StringBuilder(3 - insertionPoint + intString.Length);
            buf.Append(Sign < 0 ? "-0." : "0.");
            for (var i = 0; i < -insertionPoint; i++)
                buf.Append('0');
            buf.Append(intString);
        }
        if (_scale == 0)
            buf.Append("0");
        return buf.ToString();
    }
    public static BigDecimal Parse(string value)
    {
        return new BigDecimal(value);
    }
    public byte[] ToByteArray()
    {
        var unscaledValue = _unscaledValue.ToByteArray();
        var scale         = BitConverter.GetBytes(_scale);
        var bytes         = new byte[unscaledValue.Length + scale.Length];
        Array.Copy(unscaledValue, 0, bytes, 0,                    unscaledValue.Length);
        Array.Copy(scale,         0, bytes, unscaledValue.Length, scale.Length);
        return bytes;
    }
    public (byte[] unscaledValue, byte[] scale) ToByteArrays()
    {
        return (_unscaledValue.ToByteArray(), BitConverter.GetBytes(_scale));
    }
    public static BigDecimal Abs(BigDecimal value)
    {
        return value._unscaledValue.Sign < 0 ? -value : value;
    }
    public static BigDecimal Negate(BigDecimal value)
    {
        return new BigDecimal(BigInteger.Negate(value._unscaledValue), value.Scale);
    }
    public static BigDecimal Add(BigDecimal x, BigDecimal y)
    {
        return x + y;
    }
    public static BigDecimal Subtract(BigDecimal x, BigDecimal y)
    {
        return x - y;
    }
    public static BigDecimal Multiply(BigDecimal x, BigDecimal y)
    {
        return x * y;
    }
    public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor)
    {
        return dividend / divisor;
    }
    public static BigDecimal Pow(BigDecimal baseValue, BigInteger exponent)
    {
        if (exponent.Sign == 0)
            return One;
        if (exponent.Sign < 0)
        {
            if (baseValue == Zero)
                throw new Exception("Cannot raise zero to a negative power.");
            baseValue = One / baseValue;
            exponent  = BigInteger.Negate(exponent);
        }
        var result = baseValue;
        while (exponent > BigInteger.One)
        {
            result *= baseValue;
            exponent--;
        }
        return result;
    }
    public static int Compare(BigDecimal r1, BigDecimal r2)
    {
        return (r1 - r2)._unscaledValue.Sign;
    }
    public static bool operator ==(BigDecimal x, BigDecimal y)
    {
        return x.Equals(y);
    }
    public static bool operator !=(BigDecimal x, BigDecimal y)
    {
        return !x.Equals(y);
    }
    public static bool operator <(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) < 0;
    }
    public static bool operator <=(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) <= 0;
    }
    public static bool operator >(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) > 0;
    }
    public static bool operator >=(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) >= 0;
    }
    public static BigDecimal operator +(BigDecimal value)
    {
        return value;
    }
    public static BigDecimal operator -(BigDecimal value)
    {
        return new BigDecimal(-value._unscaledValue, value.Scale);
    }
    public static BigDecimal operator ++(BigDecimal value)
    {
        return value + One;
    }
    public static BigDecimal operator --(BigDecimal value)
    {
        return value - One;
    }
    public static BigDecimal operator +(BigDecimal left, BigDecimal right)
    {
        BigDecimal ret;
        if (left.Scale >= right.Scale)
        {
            ret = left;
        }
        else
        {
            var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
            ret = new BigDecimal(value, right.Scale);
        }
        BigDecimal ret1;
        if (right.Scale >= left.Scale)
        {
            ret1 = right;
        }
        else
        {
            var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
            ret1 = new BigDecimal(value1, left.Scale);
        }
        return new BigDecimal(ret._unscaledValue + ret1._unscaledValue, ret.Scale);
    }
    public static BigDecimal operator -(BigDecimal left, BigDecimal right)
    {
        BigDecimal ret;
        if (left.Scale >= right.Scale)
        {
            ret = left;
        }
        else
        {
            var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
            ret = new BigDecimal(value, right.Scale);
        }
        BigDecimal ret1;
        if (right.Scale >= left.Scale)
        {
            ret1 = right;
        }
        else
        {
            var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
            ret1 = new BigDecimal(value1, left.Scale);
        }
        return new BigDecimal(ret._unscaledValue - ret1._unscaledValue, ret.Scale);
    }
    public static BigDecimal operator *(BigDecimal left, BigDecimal right)
    {
        return new BigDecimal(left._unscaledValue * right._unscaledValue, left.Scale + right.Scale);
    }
    public static BigDecimal operator /(BigDecimal left, BigDecimal right)
    {
        var value = left._unscaledValue;
        var scale = left.Scale;
        while (scale > 0 && value % 10 == 0)
        {
            value /= 10;
            scale--;
        }
        var v1     = new BigDecimal(value, scale);
        var value1 = right._unscaledValue;
        var scale1 = right.Scale;
        while (scale1 > 0 && value1 % 10 == 0)
        {
            value1 /= 10;
            scale1--;
        }
        var v2 = new BigDecimal(value1, scale1);
        while (v1.Scale > 0 || v2.Scale > 0)
        {
            if (v1.Scale > 0)
                v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
            else
                v1 = new BigDecimal(v1._unscaledValue * 10, 0);
            if (v2.Scale > 0)
                v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
            else
                v2 = new BigDecimal(v2._unscaledValue * 10, 0);
        }
        var factor  = 0;
        var v1Value = v1._unscaledValue;
        while (factor < (int)MaxPrecision && v1Value % v2._unscaledValue != 0)
        {
            v1Value *= 10;
            factor++;
        }
        return new BigDecimal(v1Value / v2._unscaledValue, factor);
    }
    public static BigDecimal operator %(BigDecimal left, BigDecimal right)
    {
        var value = left._unscaledValue;
        var scale = left.Scale;
        while (scale > 0 && value % 10 == 0)
        {
            value /= 10;
            scale--;
        }
        var v1     = new BigDecimal(value, scale);
        var value1 = right._unscaledValue;
        var scale1 = right.Scale;
        while (scale1 > 0 && value1 % 10 == 0)
        {
            value1 /= 10;
            scale1--;
        }
        var v2 = new BigDecimal(value1, scale1);
        while (v1.Scale > 0 || v2.Scale > 0)
        {
            if (v1.Scale > 0)
                v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
            else
                v1 = new BigDecimal(v1._unscaledValue * 10, 0);
            if (v2.Scale > 0)
                v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
            else
                v2 = new BigDecimal(v2._unscaledValue * 10, 0);
        }
        return new BigDecimal(v1._unscaledValue % v2._unscaledValue);
    }
    public static explicit operator sbyte(BigDecimal value)
    {
        return (sbyte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator ushort(BigDecimal value)
    {
        return (ushort)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator uint(BigDecimal value)
    {
        return (uint)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator ulong(BigDecimal value)
    {
        return (ulong)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator byte(BigDecimal value)
    {
        return (byte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator short(BigDecimal value)
    {
        return (short)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator int(BigDecimal value)
    {
        return (int)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator long(BigDecimal value)
    {
        return (long)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator BigInteger(BigDecimal value)
    {
        return BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator xIntX(BigDecimal value)
    {
        return xIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator BigIntX(BigDecimal value)
    {
        return BigIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator float(BigDecimal value)
    {
        return (float)(double)value;
    }
    public static explicit operator double(BigDecimal value)
    {
        var factor = BigInteger.Pow(10, value.Scale);
        if (SafeCastToDouble(value._unscaledValue) && SafeCastToDouble(factor))
            return (double)value._unscaledValue / (double)factor;
        var dnv = value._unscaledValue * DoublePrecision / factor;
        if (dnv.IsZero)
            return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d;
        double result   = 0;
        var    isDouble = false;
        var    scale    = 308;
        while (scale > 0)
        {
            if (!isDouble)
            {
                if (SafeCastToDouble(dnv))
                {
                    result   = (double)dnv;
                    isDouble = true;
                }
                else
                {
                    dnv /= 10;
                }
            }
            result /= 10;
            scale--;
        }
        if (!isDouble)
            return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity;
        return result;
    }
    public static explicit operator decimal(BigDecimal value)
    {
        var factor = BigInteger.Pow(10, value.Scale);
        if (SafeCastToDecimal(value._unscaledValue) && SafeCastToDecimal(factor))
            return (decimal)value._unscaledValue / (decimal)factor;
        var dnv = value._unscaledValue * DecimalPrecision / factor;
        if (dnv.IsZero)
            return decimal.Zero;
        for (var scale = 28; scale >= 0; scale--)
            if (!SafeCastToDecimal(dnv))
            {
                dnv /= 10;
            }
            else
            {
                var dec = new DecimalUInt32();
                dec.dec   = (decimal)dnv;
                dec.flags = (dec.flags & ~0x00FF0000) | (scale << 16);
                return dec.dec;
            }
        throw new Exception("Value was either too large or too small for a Decimal.");
    }
    public static implicit operator BigDecimal(sbyte value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(ushort value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(uint value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(ulong value)
    {
        return new BigDecimal((BigInteger)value);
    }
    public static implicit operator BigDecimal(byte value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(short value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(int value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(long value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigInteger value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(xIntX value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigIntX value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(float value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(double value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(decimal value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigRational value)
    {
        return new BigDecimal(value);
    }
    private static bool SafeCastToDouble(BigInteger value)
    {
        return DoubleMinValue <= value && value <= DoubleMaxValue;
    }
    private static bool SafeCastToDecimal(BigInteger value)
    {
        return DecimalMinValue <= value && value <= DecimalMaxValue;
    }
    private static BigInteger GetLastDigit(BigInteger value)
    {
        return value % new BigInteger(10);
    }
    private static int GetNumberOfDigits(BigInteger value)
    {
        return BigInteger.Abs(value).ToString().Length;
    }
    private static BigDecimal Factorial(BigDecimal x)
    {
        BigDecimal r = 1;
        BigDecimal c = 1;
        while (c <= x)
        {
            r *= c;
            c++;
        }
        return r;
    }
    public static BigDecimal Exp(BigDecimal x)
    {
        BigDecimal r  = 0;
        BigDecimal r1 = 0;
        var        k  = 0;
        while (true)
        {
            r += Pow(x, k) / Factorial(k);
            if (r == r1)
                break;
            r1 = r;
            k++;
        }
        return r;
    }
    public static BigDecimal Sine(BigDecimal ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigDecimal[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigDecimal(0, 0);
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        var sin = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= Factorials[i * 2];
            if ((i & 1) == 1)
                sin -= trm;
            else
                sin += trm;
        }
        return sin;
    }
    public static BigDecimal Atan(BigDecimal ar, int n)
    {
        var atan = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= i * 2;
            if ((i & 1) == 1)
                atan -= trm;
            else
                atan += trm;
        }
        return atan;
    }
    public static BigDecimal Cosine(BigDecimal ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigDecimal[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigDecimal(0, 0);
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        BigDecimal cos = 1.0;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2);
            trm /= Factorials[i * 2 - 1];
            if ((i & 1) == 1)
                cos -= trm;
            else
                cos += trm;
        }
        return cos;
    }
    public static BigDecimal GetE(int n)
    {
        BigDecimal e = 1.0;
        var        c = n;
        while (c > 0)
        {
            BigDecimal f = 0;
            if (c == 1)
            {
                f = 1;
            }
            else
            {
                var i = c - 1;
                f = c;
                while (i > 0)
                {
                    f *= i;
                    i--;
                }
            }
            c--;
            e += 1.0 / f;
        }
        return e;
    }
    public static BigDecimal Tangent(BigDecimal ar, int n)
    {
        return Sine(ar, n) / Cosine(ar, n);
    }
    public static BigDecimal CoTangent(BigDecimal ar, int n)
    {
        return Cosine(ar, n) / Sine(ar, n);
    }
    public static BigDecimal Secant(BigDecimal ar, int n)
    {
        return 1.0 / Cosine(ar, n);
    }
    public static BigDecimal NthRoot(BigDecimal value, int nth)
    {
        BigDecimal lx;
        var        a = value;
        var        n = nth;
        BigDecimal s = 1.0;
        do
        {
            var t = s;
            lx = a / Pow(s, n - 1);
            var r = (n        - 1) * s;
            s = (lx + r) / n;
        } while (lx != s);
        return s;
    }
    public static BigDecimal LogN(BigDecimal value)
    {
        var        E = GetE(MaxFactorials);
        BigDecimal a;
        var        p = value;
        BigDecimal n = 0.0;
        while (p >= E)
        {
            p /= E;
            n++;
        }
        n += p / E;
        p =  value;
        do
        {
            a = n;
            var lx = p         / Exp(n - 1.0);
            var r  = (n - 1.0) * E;
            n = (lx + r) / E;
        } while (n != a);
        return n;
    }
    public static BigDecimal Log(BigDecimal n, int b)
    {
        return LogN(n) / LogN(b);
    }
    public static BigDecimal CoSecant(BigDecimal ar, int n)
    {
        return 1.0 / Sine(ar, n);
    }
    public static BigDecimal Ceiling(BigDecimal value, int precision)
    {
        var v1 = new BigDecimal(value);
        v1 = RemoveTrailingZeros(v1);
        var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
                v1.Scale--;
            }
            if (v1._unscaledValue.Sign < 0)
                v1._unscaledValue--;
            else
                v1._unscaledValue++;
        }
        return v1;
    }
    public static BigDecimal Floor(BigDecimal value, int precision)
    {
        var v1 = new BigDecimal(value);
        v1 = RemoveTrailingZeros(v1);
        var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
                v1.Scale--;
            }
            if (v1._unscaledValue.Sign > 0)
                v1._unscaledValue--;
            else
                v1._unscaledValue++;
        }
        return v1;
    }
    private static BigDecimal RemoveTrailingZeros(BigDecimal value)
    {
        var        v1 = new BigDecimal(value);
        BigInteger remainder;
        do
        {
            var shortened = BigInteger.DivRem(v1._unscaledValue, 10, out remainder);
            if (remainder == BigInteger.Zero)
            {
                v1._unscaledValue = shortened;
                v1.Scale--;
            }
        } while (remainder == BigInteger.Zero);
        return v1;
    }
    public BigDecimal Min(BigDecimal value)
    {
        return CompareTo(value) <= 0 ? this : value;
    }
    public BigDecimal Max(BigDecimal value)
    {
        return CompareTo(value) >= 0 ? this : value;
    }
    public static BigDecimal Sqrt(BigDecimal value)
    {
        return BigRational.Sqrt(value);
    }
    [StructLayout(LayoutKind.Explicit)]
    internal struct DecimalUInt32
    {
        [FieldOffset(0)] public decimal dec;
        [FieldOffset(0)] public int     flags;
    }
}

NativeWin32.cs

Contains some native Windows and SetupDi API calls

using System;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// Contains some native Windows and SetupDi API calls
/// </summary>
public class NativeWin32
{
[Flags]
public enum DeviceCapabilities
{
Unknown = 0x00000000,
LockSupported = 0x00000001,
EjectSupported = 0x00000002,
Removable = 0x00000004,
DockDevice = 0x00000008,
UniqueId = 0x00000010,
SilentInstall = 0x00000020,
RawDeviceOk = 0x00000040,
SurpriseRemovalOk = 0x00000080,
HardwareDisabled = 0x00000100,
NonDynamic = 0x00000200
}
[Flags]
public enum DICFG
{
/// <summary>
/// Return only the device that is associated with the system default device interface, if one is set.
/// </summary>
DEFAULT = 0x00000001,
/// <summary>
/// Return only devices that are currently present in a system.
/// </summary>
PRESENT = 0x00000002,
/// <summary>
/// Return a list of installed devices for all device setup classes or all device interface classes.
/// </summary>
ALLCLASSES = 0x00000004,
/// <summary>
/// Return only devices that are a part of the current hardware profile.
/// </summary>
PROFILE = 0x00000008,
/// <summary>
/// Return devices that support device interfaces for the specified device interface classes.
/// </summary>
DEVICEINTERFACE = 0x00000010
}
public enum PNP_VETO_TYPE
{
Ok,
TypeUnknown,
LegacyDevice,
PendingClose,
WindowsApp,
WindowsService,
OutstandingOpen,
Device,
Driver,
IllegalDeviceRequest,
InsufficientPower,
NonDisableable,
LegacyDriver
}
/// <summary>
/// SEE: https://msdn.microsoft.com/en-us/library/windows/hardware/ff542548(v=vs.85).aspx
/// </summary>
[Flags]
public enum SPDRP
{
/// <summary>
/// Requests a string describing the device, such as "Microsoft PS/2 Port Mouse", typically defined by the
/// manufacturer.
/// String
/// </summary>
DeviceDesc = 0x00000000,
/// <summary>
/// Requests the hardware IDs provided by the device that identify the device.
/// String[]
/// </summary>
HardwareId = 0x00000001,
/// <summary>
/// Requests the compatible IDs reported by the device.
/// string[]
/// </summary>
CompatibleIds = 0x00000002,
Unused0 = 0x00000003,
/// <summary>
/// Service device property represents the name of the service that is installed for a device instance.
/// string
/// </summary>
Service = 0x00000004,
Unused1 = 0x00000005,
Unused2 = 0x00000006,
/// <summary>
/// Requests the name of the device's setup class, in text format.
/// string
/// </summary>
Class = 0x00000007,
/// <summary>
/// Requests the GUID for the device's setup class.
/// GUID
/// </summary>
ClassGuid = 0x00000008,
/// <summary>
/// Requests the name of the driver-specific registry key.
/// string
/// </summary>
Driver = 0x00000009,
/// <summary>
/// ConfigFlags device property represents the configuration flags that are set for a device instance.
/// Int32
/// </summary>
ConfigFlags = 0x0000000A,
/// <summary>
/// Requests a string identifying the manufacturer of the device.
/// string
/// </summary>
Mfg = 0x0000000B,
/// <summary>
/// Requests a string that can be used to distinguish between two similar devices, typically defined by the class
/// installer.
/// string
/// </summary>
FriendlyName = 0x0000000C,
/// <summary>
/// Requests information about the device's location on the bus; the interpretation of this information is
/// bus-specific.
/// string
/// </summary>
LocationInformation = 0x0000000D,
/// <summary>
/// Requests the name of the PDO for this device.
/// Binary
/// </summary>
PhysicalDeviceObjectName = 0x0000000E,
/// <summary>
/// Capabilities device property represents the capabilities of a device instance.
/// Int32
/// </summary>
Capabilities = 0x0000000F,
/// <summary>
/// Requests a number associated with the device that can be displayed in the user interface.
/// Int32
/// </summary>
UiNumber = 0x00000010,
/// <summary>
/// UpperFilters device property represents a list of the service names of the upper-level filter drivers that are
/// installed for a device instance.
/// string[]
/// </summary>
UpperFilters = 0x00000011,
/// <summary>
/// LowerFilters device property represents a list of the service names of the lower-level filter drivers that are
/// installed for a device instance.
/// string[]
/// </summary>
LowerFilters = 0x00000012,
/// <summary>
/// Requests the GUID for the bus that the device is connected to.
/// GUID
/// </summary>
BusTypeGuid = 0x00000013,
/// <summary>
/// Requests the bus type, such as PCIBus or PCMCIABus.
/// Int32
/// </summary>
LegacyBusType = 0x00000014,
/// <summary>
/// Requests the legacy bus number of the bus the device is connected to.
/// Int32
/// </summary>
BusNumber = 0x00000015,
/// <summary>
/// Requests the name of the enumerator for the device, such as "USB".
/// string
/// </summary>
EnumeratorName = 0x00000016,
/// <summary>
/// Security device property represents a security descriptor structure for a device instance.
/// SECURITY_DESCRIPTOR
/// </summary>
Security = 0x00000017,
/// <summary>
/// SecuritySDS device property represents a security descriptor string for a device instance.
/// </summary>
Security_SDS = 0x00000018,
/// <summary>
/// DevType device property represents the device type of a device instance.
/// Int32
/// </summary>
DevType = 0x00000019,
/// <summary>
/// Exclusive device property represents a Boolean value that determines whether a device instance can be opened for
/// exclusive use.
/// bool
/// </summary>
Exclusive = 0x0000001A,
/// <summary>
/// Characteristics device property represents the characteristics of a device instance.
/// INt32
/// </summary>
Characteristics = 0x0000001B,
/// <summary>
/// Requests the address of the device on the bus.
/// Int32
/// </summary>
Address = 0x0000001C,
/// <summary>
/// UINumberDescFormat device property represents a printf-compatible format string that you should use to display
/// the value of the DEVPKEY_DEVICE_UINumber device property for a device instance.
/// string
/// </summary>
UI_Number_Desc_Format = 0x0000001D,
/// <summary>
/// PowerData device property represents power information about a device instance.
/// Binary
/// </summary>
Device_Power_Data = 0x0000001E,
/// <summary>
/// (Windows XP and later.) Requests the device's current removal policy. The operating system uses this value as a
/// hint to determine how the device is normally removed.
/// Int32
/// </summary>
RemovalPolicy = 0x0000001F,
/// <summary>
/// RemovalPolicyDefault device property represents the default removal policy for a device instance.
/// Int32
/// </summary>
Removal_Policy_Defualt = 0x00000020,
/// <summary>
/// RemovalPolicyOverride device property represents the removal policy override for a device instance.
/// Int32
/// </summary>
Removal_Policy_Override = 0x00000021,
/// <summary>
/// Windows XP and later.) Requests the device's installation state.
/// Int32
/// </summary>
InstallState = 0x00000022,
/// <summary>
/// LocationPaths device property represents the location of a device instance in the device tree.
/// string[]
/// </summary>
LocationPaths = 0x00000023
}
public const int INVALID_HANDLE_VALUE = -1;
public const int MAX_DEV_LEN = 200;
public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
public const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001;
public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004;
public const int DBT_DEVTYP_DEVICEINTERFACE = 0x00000005;
public const int DBT_DEVNODES_CHANGED = 0x0007;
public const int WM_DEVICECHANGE = 0x0219;
public const int DIF_PROPERTYCHANGE = 0x00000012;
public const int DICS_FLAG_GLOBAL = 0x00000001;
public const int DICS_FLAG_CONFIGSPECIFIC = 0x00000002;
public const int DICS_ENABLE = 0x00000001;
public const int DICS_DISABLE = 0x00000002;
public const int ERROR_INVALID_DATA = 13;
public const int ERROR_NO_MORE_ITEMS = 259;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
public const int GENERIC_READ = unchecked((int) 0x80000000);
public const int FILE_SHARE_READ = 0x00000001;
public const int FILE_SHARE_WRITE = 0x00000002;
public const int OPEN_EXISTING = 3;
public const int IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000;
[DllImport("setupapi.dll")]
public static extern bool SetupDiOpenDeviceInfo(
IntPtr deviceInfoSet,
string deviceInstanceId,
IntPtr hwndParent,
int openFlags,
ref SP_DEVINFO_DATA deviceInfoData
);
[DllImport("Kernel32.dll", SetLastError = true)]
internal static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("Kernel32.dll", SetLastError = true)]
internal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer,
int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);
[DllImport("Kernel32.dll", SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetVolumeNameForVolumeMountPoint(
string volumeName,
StringBuilder uniqueVolumeName,
int uniqueNameBufferCapacity);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient,
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter, uint Flags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern uint UnregisterDeviceNotification(IntPtr hHandle);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPStr)] string Enumerator, IntPtr hwndParent, DICFG Flags);
[DllImport("setupapi.dll")]
public static extern IntPtr SetupDiGetClassDevsEx(ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPStr)] string Enumerator,
IntPtr hwndParent, int Flags, IntPtr DeviceInfoSet,
[MarshalAs(UnmanagedType.LPStr)] string MachineName,
IntPtr Reserved);
[DllImport(@"setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInterfaces(
IntPtr hDevInfo,
IntPtr devInfo,
Guid interfaceClassGuid,
uint memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
);
[DllImport(@"setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInterfaces(
IntPtr hDevInfo,
ref SP_DEVINFO_DATA devInfo,
Guid interfaceClassGuid,
uint memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
);
[DllImport(@"setupapi.dll", SetLastError = true)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr hDevInfo,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
ref uint requiredSize,
ref SP_DEVINFO_DATA deviceInfoData
);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, int dwIndex, ref SP_DEVINFO_DATA devInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
uint PropertyRegDataType, StringBuilder PropertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
uint PropertyRegDataType, IntPtr propertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
SP_PROPCHANGE_PARAMS ClassInstallParams, int ClassInstallParamsSize);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiCallClassInstaller(uint InstallFunction, IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiClassNameFromGuid(ref Guid ClassGuid, StringBuilder className,
int ClassNameSize, ref int RequiredSize);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiGetClassDescription(ref Guid ClassGuid, StringBuilder classDescription,
int ClassDescriptionSize, ref int RequiredSize);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
public static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, ref int RequiredSize);
[DllImport("setupapi.dll")]
public static extern int CM_Get_Parent(
out uint pdnDevInst,
uint dnDevInst,
int ulFlags
);
[DllImport("setupapi.dll")]
public static extern int CM_Get_Device_ID(
uint dnDevInst,
StringBuilder buffer,
uint bufferLen,
int ulFlags);
[DllImport("setupapi.dll")]
public static extern int CM_Request_Device_Eject(
uint dnDevInst,
out PNP_VETO_TYPE pVetoType,
StringBuilder pszVetoName,
int ulNameLength,
uint ulFlags
);
[DllImport("setupapi.dll", EntryPoint = "CM_Request_Device_Eject")]
public static extern int CM_Request_Device_Eject_NoUi(
uint dnDevInst,
IntPtr pVetoType,
StringBuilder pszVetoName,
uint ulNameLength,
uint ulFlags
);
public class GUID_DEVINTERFACE
{
public static Guid BUS1394_CLASS_GUID = new Guid("6BDD1FC1-810F-11d0-BEC7-08002BE2092F");
public static Guid GUID_61883_CLASS = new Guid("7EBEFBC0-3200-11d2-B4C2-00A0C9697D07");
public static Guid GUID_DEVICE_APPLICATIONLAUNCH_BUTTON = new Guid("629758EE-986E-4D9E-8E47-DE27F8AB054D");
public static Guid GUID_DEVICE_BATTERY = new Guid("72631E54-78A4-11D0-BCF7-00AA00B7B32A");
public static Guid GUID_DEVICE_LID = new Guid("4AFA3D52-74A7-11d0-be5e-00A0C9062857");
public static Guid GUID_DEVICE_MEMORY = new Guid("3FD0F03D-92E0-45FB-B75C-5ED8FFB01021");
public static Guid GUID_DEVICE_MESSAGE_INDICATOR = new Guid("CD48A365-FA94-4CE2-A232-A1B764E5D8B4");
public static Guid GUID_DEVICE_PROCESSOR = new Guid("97FADB10-4E33-40AE-359C-8BEF029DBDD0");
public static Guid GUID_DEVICE_SYS_BUTTON = new Guid("4AFA3D53-74A7-11d0-be5e-00A0C9062857");
public static Guid GUID_DEVICE_THERMAL_ZONE = new Guid("4AFA3D51-74A7-11d0-be5e-00A0C9062857");
public static Guid GUID_BTHPORT_DEVICE_INTERFACE = new Guid("0850302A-B344-4fda-9BE9-90576B8D46F0");
public static Guid GUID_DEVINTERFACE_BRIGHTNESS = new Guid("FDE5BBA4-B3F9-46FB-BDAA-0728CE3100B4");
public static Guid GUID_DEVINTERFACE_DISPLAY_ADAPTER = new Guid("5B45201D-F2F2-4F3B-85BB-30FF1F953599");
public static Guid GUID_DEVINTERFACE_I2C = new Guid("2564AA4F-DDDB-4495-B497-6AD4A84163D7");
public static Guid GUID_DEVINTERFACE_IMAGE = new Guid("6BDD1FC6-810F-11D0-BEC7-08002BE2092F");
public static Guid GUID_DEVINTERFACE_MONITOR = new Guid("E6F07B5F-EE97-4a90-B076-33F57BF4EAA7");
public static Guid GUID_DEVINTERFACE_OPM = new Guid("BF4672DE-6B4E-4BE4-A325-68A91EA49C09");
public static Guid GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL =
new Guid("1AD9E4F0-F88D-4360-BAB9-4C2D55E564CD");
public static Guid GUID_DISPLAY_DEVICE_ARRIVAL = new Guid("1CA05180-A699-450A-9A0C-DE4FBE3DDD89");
public static Guid GUID_DEVINTERFACE_HID = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
public static Guid GUID_DEVINTERFACE_KEYBOARD = new Guid("884b96c3-56ef-11d1-bc8c-00a0c91405dd");
public static Guid GUID_DEVINTERFACE_MOUSE = new Guid("378DE44C-56EF-11D1-BC8C-00A0C91405DD");
public static Guid GUID_DEVINTERFACE_MODEM = new Guid("2C7089AA-2E0E-11D1-B114-00C04FC2AAE4");
public static Guid GUID_DEVINTERFACE_NET = new Guid("CAC88484-7515-4C03-82E6-71A87ABAC361");
public static Guid GUID_DEVINTERFACE_SENSOR = new Guid(0XBA1BB692, 0X9B7A, 0X4833, 0X9A, 0X1E, 0X52, 0X5E,
0XD1, 0X34, 0XE7, 0XE2);
public static Guid GUID_DEVINTERFACE_COMPORT = new Guid("86E0D1E0-8089-11D0-9CE4-08003E301F73");
public static Guid GUID_DEVINTERFACE_PARALLEL = new Guid("97F76EF0-F883-11D0-AF1F-0000F800845C");
public static Guid GUID_DEVINTERFACE_PARCLASS = new Guid("811FC6A5-F728-11D0-A537-0000F8753ED1");
public static Guid GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR =
new Guid("4D36E978-E325-11CE-BFC1-08002BE10318");
public static Guid GUID_DEVINTERFACE_CDCHANGER = new Guid("53F56312-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_CDROM = new Guid("53F56308-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_DISK = new Guid("53F56307-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_FLOPPY = new Guid("53F56311-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_MEDIUMCHANGER = new Guid("53F56310-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_PARTITION = new Guid("53F5630A-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_STORAGEPORT = new Guid("2ACCFE60-C130-11D2-B082-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_TAPE = new Guid("53F5630B-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_VOLUME = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_DEVINTERFACE_WRITEONCEDISK = new Guid("53F5630C-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_IO_VOLUME_DEVICE_INTERFACE = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid MOUNTDEV_MOUNTED_DEVICE_GUID = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
public static Guid GUID_AVC_CLASS = new Guid("095780C3-48A1-4570-BD95-46707F78C2DC");
public static Guid GUID_VIRTUAL_AVC_CLASS = new Guid("616EF4D0-23CE-446D-A568-C31EB01913D0");
public static Guid KSCATEGORY_ACOUSTIC_ECHO_CANCEL = new Guid("BF963D80-C559-11D0-8A2B-00A0C9255AC1");
public static Guid KSCATEGORY_AUDIO = new Guid("6994AD04-93EF-11D0-A3CC-00A0C9223196");
public static Guid KSCATEGORY_AUDIO_DEVICE = new Guid("FBF6F530-07B9-11D2-A71E-0000F8004788");
public static Guid KSCATEGORY_AUDIO_GFX = new Guid("9BAF9572-340C-11D3-ABDC-00A0C90AB16F");
public static Guid KSCATEGORY_AUDIO_SPLITTER = new Guid("9EA331FA-B91B-45F8-9285-BD2BC77AFCDE");
public static Guid KSCATEGORY_BDA_IP_SINK = new Guid("71985F4A-1CA1-11d3-9CC8-00C04F7971E0");
public static Guid KSCATEGORY_BDA_NETWORK_EPG = new Guid("71985F49-1CA1-11d3-9CC8-00C04F7971E0");
public static Guid KSCATEGORY_BDA_NETWORK_PROVIDER = new Guid("71985F4B-1CA1-11d3-9CC8-00C04F7971E0");
public static Guid KSCATEGORY_BDA_NETWORK_TUNER = new Guid("71985F48-1CA1-11d3-9CC8-00C04F7971E0");
public static Guid KSCATEGORY_BDA_RECEIVER_COMPONENT = new Guid("FD0A5AF4-B41D-11d2-9C95-00C04F7971E0");
public static Guid KSCATEGORY_BDA_TRANSPORT_INFORMATION = new Guid("A2E3074F-6C3D-11d3-B653-00C04F79498E");
public static Guid KSCATEGORY_BRIDGE = new Guid("085AFF00-62CE-11CF-A5D6-28DB04C10000");
public static Guid KSCATEGORY_CAPTURE = new Guid("65E8773D-8F56-11D0-A3B9-00A0C9223196");
public static Guid KSCATEGORY_CLOCK = new Guid("53172480-4791-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_COMMUNICATIONSTRANSFORM = new Guid("CF1DDA2C-9743-11D0-A3EE-00A0C9223196");
public static Guid KSCATEGORY_CROSSBAR = new Guid("A799A801-A46D-11D0-A18C-00A02401DCD4");
public static Guid KSCATEGORY_DATACOMPRESSOR = new Guid("1E84C900-7E70-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_DATADECOMPRESSOR = new Guid("2721AE20-7E70-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_DATATRANSFORM = new Guid("2EB07EA0-7E70-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_DRM_DESCRAMBLE = new Guid("FFBB6E3F-CCFE-4D84-90D9-421418B03A8E");
public static Guid KSCATEGORY_ENCODER = new Guid("19689BF6-C384-48fd-AD51-90E58C79F70B");
public static Guid KSCATEGORY_ESCALANTE_PLATFORM_DRIVER = new Guid("74F3AEA8-9768-11D1-8E07-00A0C95EC22E");
public static Guid KSCATEGORY_FILESYSTEM = new Guid("760FED5E-9357-11D0-A3CC-00A0C9223196");
public static Guid KSCATEGORY_INTERFACETRANSFORM = new Guid("CF1DDA2D-9743-11D0-A3EE-00A0C9223196");
public static Guid KSCATEGORY_MEDIUMTRANSFORM = new Guid("CF1DDA2E-9743-11D0-A3EE-00A0C9223196");
public static Guid KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR = new Guid("830A44F2-A32D-476B-BE97-42845673B35A");
public static Guid KSCATEGORY_MIXER = new Guid("AD809C00-7B88-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_MULTIPLEXER = new Guid("7A5DE1D3-01A1-452c-B481-4FA2B96271E8");
public static Guid KSCATEGORY_NETWORK = new Guid("67C9CC3C-69C4-11D2-8759-00A0C9223196");
public static Guid KSCATEGORY_PREFERRED_MIDIOUT_DEVICE = new Guid("D6C50674-72C1-11D2-9755-0000F8004788");
public static Guid KSCATEGORY_PREFERRED_WAVEIN_DEVICE = new Guid("D6C50671-72C1-11D2-9755-0000F8004788");
public static Guid KSCATEGORY_PREFERRED_WAVEOUT_DEVICE = new Guid("D6C5066E-72C1-11D2-9755-0000F8004788");
public static Guid KSCATEGORY_PROXY = new Guid("97EBAACA-95BD-11D0-A3EA-00A0C9223196");
public static Guid KSCATEGORY_QUALITY = new Guid("97EBAACB-95BD-11D0-A3EA-00A0C9223196");
public static Guid KSCATEGORY_REALTIME = new Guid("EB115FFC-10C8-4964-831D-6DCB02E6F23F");
public static Guid KSCATEGORY_RENDER = new Guid("65E8773E-8F56-11D0-A3B9-00A0C9223196");
public static Guid KSCATEGORY_SPLITTER = new Guid("0A4252A0-7E70-11D0-A5D6-28DB04C10000");
public static Guid KSCATEGORY_SYNTHESIZER = new Guid("DFF220F3-F70F-11D0-B917-00A0C9223196");
public static Guid KSCATEGORY_SYSAUDIO = new Guid("A7C7A5B1-5AF3-11D1-9CED-00A024BF0407");
public static Guid KSCATEGORY_TEXT = new Guid("6994AD06-93EF-11D0-A3CC-00A0C9223196");
public static Guid KSCATEGORY_TOPOLOGY = new Guid("DDA54A40-1E4C-11D1-A050-405705C10000");
public static Guid KSCATEGORY_TVAUDIO = new Guid("A799A802-A46D-11D0-A18C-00A02401DCD4");
public static Guid KSCATEGORY_TVTUNER = new Guid("A799A800-A46D-11D0-A18C-00A02401DCD4");
public static Guid KSCATEGORY_VBICODEC = new Guid("07DAD660-22F1-11D1-A9F4-00C04FBBDE8F");
public static Guid KSCATEGORY_VIDEO = new Guid("6994AD05-93EF-11D0-A3CC-00A0C9223196");
public static Guid KSCATEGORY_VIRTUAL = new Guid("3503EAC4-1F26-11D1-8AB0-00A0C9223196");
public static Guid KSCATEGORY_VPMUX = new Guid("A799A803-A46D-11D0-A18C-00A02401DCD4");
public static Guid KSCATEGORY_WDMAUD = new Guid("3E227E76-690D-11D2-8161-0000F8775BF1");
public static Guid KSMFT_CATEGORY_AUDIO_DECODER = new Guid("9ea73fb4-ef7a-4559-8d5d-719d8f0426c7");
public static Guid KSMFT_CATEGORY_AUDIO_EFFECT = new Guid("11064c48-3648-4ed0-932e-05ce8ac811b7");
public static Guid KSMFT_CATEGORY_AUDIO_ENCODER = new Guid("91c64bd0-f91e-4d8c-9276-db248279d975");
public static Guid KSMFT_CATEGORY_DEMULTIPLEXER = new Guid("a8700a7a-939b-44c5-99d7-76226b23b3f1");
public static Guid KSMFT_CATEGORY_MULTIPLEXER = new Guid("059c561e-05ae-4b61-b69d-55b61ee54a7b");
public static Guid KSMFT_CATEGORY_OTHER = new Guid("90175d57-b7ea-4901-aeb3-933a8747756f");
public static Guid KSMFT_CATEGORY_VIDEO_DECODER = new Guid("d6c02d4b-6833-45b4-971a-05a4b04bab91");
public static Guid KSMFT_CATEGORY_VIDEO_EFFECT = new Guid("12e17c21-532c-4a6e-8a1c-40825a736397");
public static Guid KSMFT_CATEGORY_VIDEO_ENCODER = new Guid("f79eac7d-e545-4387-bdee-d647d7bde42a");
public static Guid KSMFT_CATEGORY_VIDEO_PROCESSOR = new Guid("302ea3fc-aa5f-47f9-9f7a-c2188bb16302");
public static Guid GUID_DEVINTERFACE_USB_DEVICE = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED");
public static Guid GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new Guid("3ABF6F2D-71C4-462A-8A92-1E6861E6AF27");
public static Guid GUID_DEVINTERFACE_USB_HUB = new Guid("F18A0E88-C30C-11D0-8815-00A0C906BED8");
public static Guid GUID_DEVINTERFACE_WPD = new Guid("6AC27878-A6FA-4155-BA85-F98F491D4F33");
public static Guid GUID_DEVINTERFACE_WPD_PRIVATE = new Guid("BA0C718F-4DED-49B7-BDD3-FABE28661211");
public static Guid GUID_DEVINTERFACE_SIDESHOW = new Guid("152E5811-FEB9-4B00-90F4-D32947AE1681");
}
[StructLayout(LayoutKind.Sequential)]
public struct DISK_EXTENT
{
internal uint DiskNumber;
internal ulong StartingOffset;
internal ulong ExtentLength;
}
[StructLayout(LayoutKind.Sequential)]
public class VOLUME_DISK_EXTENTS
{
public uint NumberOfDiskExtents;
public uint ZBUf;
}
[StructLayout(LayoutKind.Sequential)]
public class DEV_BROADCAST_DEVICEINTERFACE
{
public int dbcc_devicetype;
public int dbcc_reserved;
public int dbcc_size;
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
public uint cbSize;
public Guid classGuid;
public uint devInst;
public UIntPtr reserved;
}
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINSTALL_PARAMS
{
public int cbSize;
public IntPtr ClassInstallReserved;
[MarshalAs(UnmanagedType.LPTStr)] public string DriverPath;
public IntPtr FileQueue;
public int Flags;
public int FlagsEx;
public IntPtr hwndParent;
public IntPtr InstallMsgHandler;
public IntPtr InstallMsgHandlerContext;
public int Reserved;
}
[StructLayout(LayoutKind.Sequential)]
public class SP_PROPCHANGE_PARAMS
{
public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
public int HwProfile;
public int Scope;
public int StateChange;
}
[StructLayout(LayoutKind.Sequential)]
public class SP_CLASSINSTALL_HEADER
{
public int cbSize;
public int InstallFunction;
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public uint cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string devicePath;
}
[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVICE_INTERFACE_DATA
{
public uint cbSize;
public uint flags;
public Guid interfaceClassGuid;
public UIntPtr reserved;
}
}
using System; using System.Runtime.InteropServices; using System.Text; /// <summary> /// Contains some native Windows and SetupDi API calls /// </summary> public class NativeWin32 { [Flags] public enum DeviceCapabilities { Unknown = 0x00000000, LockSupported = 0x00000001, EjectSupported = 0x00000002, Removable = 0x00000004, DockDevice = 0x00000008, UniqueId = 0x00000010, SilentInstall = 0x00000020, RawDeviceOk = 0x00000040, SurpriseRemovalOk = 0x00000080, HardwareDisabled = 0x00000100, NonDynamic = 0x00000200 } [Flags] public enum DICFG { /// <summary> /// Return only the device that is associated with the system default device interface, if one is set. /// </summary> DEFAULT = 0x00000001, /// <summary> /// Return only devices that are currently present in a system. /// </summary> PRESENT = 0x00000002, /// <summary> /// Return a list of installed devices for all device setup classes or all device interface classes. /// </summary> ALLCLASSES = 0x00000004, /// <summary> /// Return only devices that are a part of the current hardware profile. /// </summary> PROFILE = 0x00000008, /// <summary> /// Return devices that support device interfaces for the specified device interface classes. /// </summary> DEVICEINTERFACE = 0x00000010 } public enum PNP_VETO_TYPE { Ok, TypeUnknown, LegacyDevice, PendingClose, WindowsApp, WindowsService, OutstandingOpen, Device, Driver, IllegalDeviceRequest, InsufficientPower, NonDisableable, LegacyDriver } /// <summary> /// SEE: https://msdn.microsoft.com/en-us/library/windows/hardware/ff542548(v=vs.85).aspx /// </summary> [Flags] public enum SPDRP { /// <summary> /// Requests a string describing the device, such as "Microsoft PS/2 Port Mouse", typically defined by the /// manufacturer. /// String /// </summary> DeviceDesc = 0x00000000, /// <summary> /// Requests the hardware IDs provided by the device that identify the device. /// String[] /// </summary> HardwareId = 0x00000001, /// <summary> /// Requests the compatible IDs reported by the device. /// string[] /// </summary> CompatibleIds = 0x00000002, Unused0 = 0x00000003, /// <summary> /// Service device property represents the name of the service that is installed for a device instance. /// string /// </summary> Service = 0x00000004, Unused1 = 0x00000005, Unused2 = 0x00000006, /// <summary> /// Requests the name of the device's setup class, in text format. /// string /// </summary> Class = 0x00000007, /// <summary> /// Requests the GUID for the device's setup class. /// GUID /// </summary> ClassGuid = 0x00000008, /// <summary> /// Requests the name of the driver-specific registry key. /// string /// </summary> Driver = 0x00000009, /// <summary> /// ConfigFlags device property represents the configuration flags that are set for a device instance. /// Int32 /// </summary> ConfigFlags = 0x0000000A, /// <summary> /// Requests a string identifying the manufacturer of the device. /// string /// </summary> Mfg = 0x0000000B, /// <summary> /// Requests a string that can be used to distinguish between two similar devices, typically defined by the class /// installer. /// string /// </summary> FriendlyName = 0x0000000C, /// <summary> /// Requests information about the device's location on the bus; the interpretation of this information is /// bus-specific. /// string /// </summary> LocationInformation = 0x0000000D, /// <summary> /// Requests the name of the PDO for this device. /// Binary /// </summary> PhysicalDeviceObjectName = 0x0000000E, /// <summary> /// Capabilities device property represents the capabilities of a device instance. /// Int32 /// </summary> Capabilities = 0x0000000F, /// <summary> /// Requests a number associated with the device that can be displayed in the user interface. /// Int32 /// </summary> UiNumber = 0x00000010, /// <summary> /// UpperFilters device property represents a list of the service names of the upper-level filter drivers that are /// installed for a device instance. /// string[] /// </summary> UpperFilters = 0x00000011, /// <summary> /// LowerFilters device property represents a list of the service names of the lower-level filter drivers that are /// installed for a device instance. /// string[] /// </summary> LowerFilters = 0x00000012, /// <summary> /// Requests the GUID for the bus that the device is connected to. /// GUID /// </summary> BusTypeGuid = 0x00000013, /// <summary> /// Requests the bus type, such as PCIBus or PCMCIABus. /// Int32 /// </summary> LegacyBusType = 0x00000014, /// <summary> /// Requests the legacy bus number of the bus the device is connected to. /// Int32 /// </summary> BusNumber = 0x00000015, /// <summary> /// Requests the name of the enumerator for the device, such as "USB". /// string /// </summary> EnumeratorName = 0x00000016, /// <summary> /// Security device property represents a security descriptor structure for a device instance. /// SECURITY_DESCRIPTOR /// </summary> Security = 0x00000017, /// <summary> /// SecuritySDS device property represents a security descriptor string for a device instance. /// </summary> Security_SDS = 0x00000018, /// <summary> /// DevType device property represents the device type of a device instance. /// Int32 /// </summary> DevType = 0x00000019, /// <summary> /// Exclusive device property represents a Boolean value that determines whether a device instance can be opened for /// exclusive use. /// bool /// </summary> Exclusive = 0x0000001A, /// <summary> /// Characteristics device property represents the characteristics of a device instance. /// INt32 /// </summary> Characteristics = 0x0000001B, /// <summary> /// Requests the address of the device on the bus. /// Int32 /// </summary> Address = 0x0000001C, /// <summary> /// UINumberDescFormat device property represents a printf-compatible format string that you should use to display /// the value of the DEVPKEY_DEVICE_UINumber device property for a device instance. /// string /// </summary> UI_Number_Desc_Format = 0x0000001D, /// <summary> /// PowerData device property represents power information about a device instance. /// Binary /// </summary> Device_Power_Data = 0x0000001E, /// <summary> /// (Windows XP and later.) Requests the device's current removal policy. The operating system uses this value as a /// hint to determine how the device is normally removed. /// Int32 /// </summary> RemovalPolicy = 0x0000001F, /// <summary> /// RemovalPolicyDefault device property represents the default removal policy for a device instance. /// Int32 /// </summary> Removal_Policy_Defualt = 0x00000020, /// <summary> /// RemovalPolicyOverride device property represents the removal policy override for a device instance. /// Int32 /// </summary> Removal_Policy_Override = 0x00000021, /// <summary> /// Windows XP and later.) Requests the device's installation state. /// Int32 /// </summary> InstallState = 0x00000022, /// <summary> /// LocationPaths device property represents the location of a device instance in the device tree. /// string[] /// </summary> LocationPaths = 0x00000023 } public const int INVALID_HANDLE_VALUE = -1; public const int MAX_DEV_LEN = 200; public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000; public const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001; public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004; public const int DBT_DEVTYP_DEVICEINTERFACE = 0x00000005; public const int DBT_DEVNODES_CHANGED = 0x0007; public const int WM_DEVICECHANGE = 0x0219; public const int DIF_PROPERTYCHANGE = 0x00000012; public const int DICS_FLAG_GLOBAL = 0x00000001; public const int DICS_FLAG_CONFIGSPECIFIC = 0x00000002; public const int DICS_ENABLE = 0x00000001; public const int DICS_DISABLE = 0x00000002; public const int ERROR_INVALID_DATA = 13; public const int ERROR_NO_MORE_ITEMS = 259; public const int ERROR_INSUFFICIENT_BUFFER = 122; public const int GENERIC_READ = unchecked((int) 0x80000000); public const int FILE_SHARE_READ = 0x00000001; public const int FILE_SHARE_WRITE = 0x00000002; public const int OPEN_EXISTING = 3; public const int IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000; [DllImport("setupapi.dll")] public static extern bool SetupDiOpenDeviceInfo( IntPtr deviceInfoSet, string deviceInstanceId, IntPtr hwndParent, int openFlags, ref SP_DEVINFO_DATA deviceInfoData ); [DllImport("Kernel32.dll", SetLastError = true)] internal static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("Kernel32.dll", SetLastError = true)] internal static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped); [DllImport("Kernel32.dll", SetLastError = true)] internal static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] internal static extern bool GetVolumeNameForVolumeMountPoint( string volumeName, StringBuilder uniqueVolumeName, int uniqueNameBufferCapacity); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, DEV_BROADCAST_DEVICEINTERFACE NotificationFilter, uint Flags); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern uint UnregisterDeviceNotification(IntPtr hHandle); [DllImport("setupapi.dll", SetLastError = true)] public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPStr)] string Enumerator, IntPtr hwndParent, DICFG Flags); [DllImport("setupapi.dll")] public static extern IntPtr SetupDiGetClassDevsEx(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPStr)] string Enumerator, IntPtr hwndParent, int Flags, IntPtr DeviceInfoSet, [MarshalAs(UnmanagedType.LPStr)] string MachineName, IntPtr Reserved); [DllImport(@"setupapi.dll", SetLastError = true)] public static extern bool SetupDiEnumDeviceInterfaces( IntPtr hDevInfo, IntPtr devInfo, Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData ); [DllImport(@"setupapi.dll", SetLastError = true)] public static extern bool SetupDiEnumDeviceInterfaces( IntPtr hDevInfo, ref SP_DEVINFO_DATA devInfo, Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData ); [DllImport(@"setupapi.dll", SetLastError = true)] public static extern bool SetupDiGetDeviceInterfaceDetail( IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, ref uint requiredSize, ref SP_DEVINFO_DATA deviceInfoData ); [DllImport("setupapi.dll", SetLastError = true)] public static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet); [DllImport("setupapi.dll", SetLastError = true)] public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, int dwIndex, ref SP_DEVINFO_DATA devInfoData); [DllImport("setupapi.dll", SetLastError = true)] public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, uint PropertyRegDataType, StringBuilder PropertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize); [DllImport("setupapi.dll", SetLastError = true)] public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, uint PropertyRegDataType, IntPtr propertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize); [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, SP_PROPCHANGE_PARAMS ClassInstallParams, int ClassInstallParamsSize); [DllImport("setupapi.dll", CharSet = CharSet.Auto)] public static extern bool SetupDiCallClassInstaller(uint InstallFunction, IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData); [DllImport("setupapi.dll", CharSet = CharSet.Auto)] public static extern bool SetupDiClassNameFromGuid(ref Guid ClassGuid, StringBuilder className, int ClassNameSize, ref int RequiredSize); [DllImport("setupapi.dll", CharSet = CharSet.Auto)] public static extern bool SetupDiGetClassDescription(ref Guid ClassGuid, StringBuilder classDescription, int ClassDescriptionSize, ref int RequiredSize); [DllImport("setupapi.dll", CharSet = CharSet.Auto)] public static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, ref int RequiredSize); [DllImport("setupapi.dll")] public static extern int CM_Get_Parent( out uint pdnDevInst, uint dnDevInst, int ulFlags ); [DllImport("setupapi.dll")] public static extern int CM_Get_Device_ID( uint dnDevInst, StringBuilder buffer, uint bufferLen, int ulFlags); [DllImport("setupapi.dll")] public static extern int CM_Request_Device_Eject( uint dnDevInst, out PNP_VETO_TYPE pVetoType, StringBuilder pszVetoName, int ulNameLength, uint ulFlags ); [DllImport("setupapi.dll", EntryPoint = "CM_Request_Device_Eject")] public static extern int CM_Request_Device_Eject_NoUi( uint dnDevInst, IntPtr pVetoType, StringBuilder pszVetoName, uint ulNameLength, uint ulFlags ); public class GUID_DEVINTERFACE { public static Guid BUS1394_CLASS_GUID = new Guid("6BDD1FC1-810F-11d0-BEC7-08002BE2092F"); public static Guid GUID_61883_CLASS = new Guid("7EBEFBC0-3200-11d2-B4C2-00A0C9697D07"); public static Guid GUID_DEVICE_APPLICATIONLAUNCH_BUTTON = new Guid("629758EE-986E-4D9E-8E47-DE27F8AB054D"); public static Guid GUID_DEVICE_BATTERY = new Guid("72631E54-78A4-11D0-BCF7-00AA00B7B32A"); public static Guid GUID_DEVICE_LID = new Guid("4AFA3D52-74A7-11d0-be5e-00A0C9062857"); public static Guid GUID_DEVICE_MEMORY = new Guid("3FD0F03D-92E0-45FB-B75C-5ED8FFB01021"); public static Guid GUID_DEVICE_MESSAGE_INDICATOR = new Guid("CD48A365-FA94-4CE2-A232-A1B764E5D8B4"); public static Guid GUID_DEVICE_PROCESSOR = new Guid("97FADB10-4E33-40AE-359C-8BEF029DBDD0"); public static Guid GUID_DEVICE_SYS_BUTTON = new Guid("4AFA3D53-74A7-11d0-be5e-00A0C9062857"); public static Guid GUID_DEVICE_THERMAL_ZONE = new Guid("4AFA3D51-74A7-11d0-be5e-00A0C9062857"); public static Guid GUID_BTHPORT_DEVICE_INTERFACE = new Guid("0850302A-B344-4fda-9BE9-90576B8D46F0"); public static Guid GUID_DEVINTERFACE_BRIGHTNESS = new Guid("FDE5BBA4-B3F9-46FB-BDAA-0728CE3100B4"); public static Guid GUID_DEVINTERFACE_DISPLAY_ADAPTER = new Guid("5B45201D-F2F2-4F3B-85BB-30FF1F953599"); public static Guid GUID_DEVINTERFACE_I2C = new Guid("2564AA4F-DDDB-4495-B497-6AD4A84163D7"); public static Guid GUID_DEVINTERFACE_IMAGE = new Guid("6BDD1FC6-810F-11D0-BEC7-08002BE2092F"); public static Guid GUID_DEVINTERFACE_MONITOR = new Guid("E6F07B5F-EE97-4a90-B076-33F57BF4EAA7"); public static Guid GUID_DEVINTERFACE_OPM = new Guid("BF4672DE-6B4E-4BE4-A325-68A91EA49C09"); public static Guid GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL = new Guid("1AD9E4F0-F88D-4360-BAB9-4C2D55E564CD"); public static Guid GUID_DISPLAY_DEVICE_ARRIVAL = new Guid("1CA05180-A699-450A-9A0C-DE4FBE3DDD89"); public static Guid GUID_DEVINTERFACE_HID = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030"); public static Guid GUID_DEVINTERFACE_KEYBOARD = new Guid("884b96c3-56ef-11d1-bc8c-00a0c91405dd"); public static Guid GUID_DEVINTERFACE_MOUSE = new Guid("378DE44C-56EF-11D1-BC8C-00A0C91405DD"); public static Guid GUID_DEVINTERFACE_MODEM = new Guid("2C7089AA-2E0E-11D1-B114-00C04FC2AAE4"); public static Guid GUID_DEVINTERFACE_NET = new Guid("CAC88484-7515-4C03-82E6-71A87ABAC361"); public static Guid GUID_DEVINTERFACE_SENSOR = new Guid(0XBA1BB692, 0X9B7A, 0X4833, 0X9A, 0X1E, 0X52, 0X5E, 0XD1, 0X34, 0XE7, 0XE2); public static Guid GUID_DEVINTERFACE_COMPORT = new Guid("86E0D1E0-8089-11D0-9CE4-08003E301F73"); public static Guid GUID_DEVINTERFACE_PARALLEL = new Guid("97F76EF0-F883-11D0-AF1F-0000F800845C"); public static Guid GUID_DEVINTERFACE_PARCLASS = new Guid("811FC6A5-F728-11D0-A537-0000F8753ED1"); public static Guid GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR = new Guid("4D36E978-E325-11CE-BFC1-08002BE10318"); public static Guid GUID_DEVINTERFACE_CDCHANGER = new Guid("53F56312-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_CDROM = new Guid("53F56308-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_DISK = new Guid("53F56307-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_FLOPPY = new Guid("53F56311-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_MEDIUMCHANGER = new Guid("53F56310-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_PARTITION = new Guid("53F5630A-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_STORAGEPORT = new Guid("2ACCFE60-C130-11D2-B082-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_TAPE = new Guid("53F5630B-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_VOLUME = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_DEVINTERFACE_WRITEONCEDISK = new Guid("53F5630C-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_IO_VOLUME_DEVICE_INTERFACE = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid MOUNTDEV_MOUNTED_DEVICE_GUID = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B"); public static Guid GUID_AVC_CLASS = new Guid("095780C3-48A1-4570-BD95-46707F78C2DC"); public static Guid GUID_VIRTUAL_AVC_CLASS = new Guid("616EF4D0-23CE-446D-A568-C31EB01913D0"); public static Guid KSCATEGORY_ACOUSTIC_ECHO_CANCEL = new Guid("BF963D80-C559-11D0-8A2B-00A0C9255AC1"); public static Guid KSCATEGORY_AUDIO = new Guid("6994AD04-93EF-11D0-A3CC-00A0C9223196"); public static Guid KSCATEGORY_AUDIO_DEVICE = new Guid("FBF6F530-07B9-11D2-A71E-0000F8004788"); public static Guid KSCATEGORY_AUDIO_GFX = new Guid("9BAF9572-340C-11D3-ABDC-00A0C90AB16F"); public static Guid KSCATEGORY_AUDIO_SPLITTER = new Guid("9EA331FA-B91B-45F8-9285-BD2BC77AFCDE"); public static Guid KSCATEGORY_BDA_IP_SINK = new Guid("71985F4A-1CA1-11d3-9CC8-00C04F7971E0"); public static Guid KSCATEGORY_BDA_NETWORK_EPG = new Guid("71985F49-1CA1-11d3-9CC8-00C04F7971E0"); public static Guid KSCATEGORY_BDA_NETWORK_PROVIDER = new Guid("71985F4B-1CA1-11d3-9CC8-00C04F7971E0"); public static Guid KSCATEGORY_BDA_NETWORK_TUNER = new Guid("71985F48-1CA1-11d3-9CC8-00C04F7971E0"); public static Guid KSCATEGORY_BDA_RECEIVER_COMPONENT = new Guid("FD0A5AF4-B41D-11d2-9C95-00C04F7971E0"); public static Guid KSCATEGORY_BDA_TRANSPORT_INFORMATION = new Guid("A2E3074F-6C3D-11d3-B653-00C04F79498E"); public static Guid KSCATEGORY_BRIDGE = new Guid("085AFF00-62CE-11CF-A5D6-28DB04C10000"); public static Guid KSCATEGORY_CAPTURE = new Guid("65E8773D-8F56-11D0-A3B9-00A0C9223196"); public static Guid KSCATEGORY_CLOCK = new Guid("53172480-4791-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_COMMUNICATIONSTRANSFORM = new Guid("CF1DDA2C-9743-11D0-A3EE-00A0C9223196"); public static Guid KSCATEGORY_CROSSBAR = new Guid("A799A801-A46D-11D0-A18C-00A02401DCD4"); public static Guid KSCATEGORY_DATACOMPRESSOR = new Guid("1E84C900-7E70-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_DATADECOMPRESSOR = new Guid("2721AE20-7E70-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_DATATRANSFORM = new Guid("2EB07EA0-7E70-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_DRM_DESCRAMBLE = new Guid("FFBB6E3F-CCFE-4D84-90D9-421418B03A8E"); public static Guid KSCATEGORY_ENCODER = new Guid("19689BF6-C384-48fd-AD51-90E58C79F70B"); public static Guid KSCATEGORY_ESCALANTE_PLATFORM_DRIVER = new Guid("74F3AEA8-9768-11D1-8E07-00A0C95EC22E"); public static Guid KSCATEGORY_FILESYSTEM = new Guid("760FED5E-9357-11D0-A3CC-00A0C9223196"); public static Guid KSCATEGORY_INTERFACETRANSFORM = new Guid("CF1DDA2D-9743-11D0-A3EE-00A0C9223196"); public static Guid KSCATEGORY_MEDIUMTRANSFORM = new Guid("CF1DDA2E-9743-11D0-A3EE-00A0C9223196"); public static Guid KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR = new Guid("830A44F2-A32D-476B-BE97-42845673B35A"); public static Guid KSCATEGORY_MIXER = new Guid("AD809C00-7B88-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_MULTIPLEXER = new Guid("7A5DE1D3-01A1-452c-B481-4FA2B96271E8"); public static Guid KSCATEGORY_NETWORK = new Guid("67C9CC3C-69C4-11D2-8759-00A0C9223196"); public static Guid KSCATEGORY_PREFERRED_MIDIOUT_DEVICE = new Guid("D6C50674-72C1-11D2-9755-0000F8004788"); public static Guid KSCATEGORY_PREFERRED_WAVEIN_DEVICE = new Guid("D6C50671-72C1-11D2-9755-0000F8004788"); public static Guid KSCATEGORY_PREFERRED_WAVEOUT_DEVICE = new Guid("D6C5066E-72C1-11D2-9755-0000F8004788"); public static Guid KSCATEGORY_PROXY = new Guid("97EBAACA-95BD-11D0-A3EA-00A0C9223196"); public static Guid KSCATEGORY_QUALITY = new Guid("97EBAACB-95BD-11D0-A3EA-00A0C9223196"); public static Guid KSCATEGORY_REALTIME = new Guid("EB115FFC-10C8-4964-831D-6DCB02E6F23F"); public static Guid KSCATEGORY_RENDER = new Guid("65E8773E-8F56-11D0-A3B9-00A0C9223196"); public static Guid KSCATEGORY_SPLITTER = new Guid("0A4252A0-7E70-11D0-A5D6-28DB04C10000"); public static Guid KSCATEGORY_SYNTHESIZER = new Guid("DFF220F3-F70F-11D0-B917-00A0C9223196"); public static Guid KSCATEGORY_SYSAUDIO = new Guid("A7C7A5B1-5AF3-11D1-9CED-00A024BF0407"); public static Guid KSCATEGORY_TEXT = new Guid("6994AD06-93EF-11D0-A3CC-00A0C9223196"); public static Guid KSCATEGORY_TOPOLOGY = new Guid("DDA54A40-1E4C-11D1-A050-405705C10000"); public static Guid KSCATEGORY_TVAUDIO = new Guid("A799A802-A46D-11D0-A18C-00A02401DCD4"); public static Guid KSCATEGORY_TVTUNER = new Guid("A799A800-A46D-11D0-A18C-00A02401DCD4"); public static Guid KSCATEGORY_VBICODEC = new Guid("07DAD660-22F1-11D1-A9F4-00C04FBBDE8F"); public static Guid KSCATEGORY_VIDEO = new Guid("6994AD05-93EF-11D0-A3CC-00A0C9223196"); public static Guid KSCATEGORY_VIRTUAL = new Guid("3503EAC4-1F26-11D1-8AB0-00A0C9223196"); public static Guid KSCATEGORY_VPMUX = new Guid("A799A803-A46D-11D0-A18C-00A02401DCD4"); public static Guid KSCATEGORY_WDMAUD = new Guid("3E227E76-690D-11D2-8161-0000F8775BF1"); public static Guid KSMFT_CATEGORY_AUDIO_DECODER = new Guid("9ea73fb4-ef7a-4559-8d5d-719d8f0426c7"); public static Guid KSMFT_CATEGORY_AUDIO_EFFECT = new Guid("11064c48-3648-4ed0-932e-05ce8ac811b7"); public static Guid KSMFT_CATEGORY_AUDIO_ENCODER = new Guid("91c64bd0-f91e-4d8c-9276-db248279d975"); public static Guid KSMFT_CATEGORY_DEMULTIPLEXER = new Guid("a8700a7a-939b-44c5-99d7-76226b23b3f1"); public static Guid KSMFT_CATEGORY_MULTIPLEXER = new Guid("059c561e-05ae-4b61-b69d-55b61ee54a7b"); public static Guid KSMFT_CATEGORY_OTHER = new Guid("90175d57-b7ea-4901-aeb3-933a8747756f"); public static Guid KSMFT_CATEGORY_VIDEO_DECODER = new Guid("d6c02d4b-6833-45b4-971a-05a4b04bab91"); public static Guid KSMFT_CATEGORY_VIDEO_EFFECT = new Guid("12e17c21-532c-4a6e-8a1c-40825a736397"); public static Guid KSMFT_CATEGORY_VIDEO_ENCODER = new Guid("f79eac7d-e545-4387-bdee-d647d7bde42a"); public static Guid KSMFT_CATEGORY_VIDEO_PROCESSOR = new Guid("302ea3fc-aa5f-47f9-9f7a-c2188bb16302"); public static Guid GUID_DEVINTERFACE_USB_DEVICE = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); public static Guid GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new Guid("3ABF6F2D-71C4-462A-8A92-1E6861E6AF27"); public static Guid GUID_DEVINTERFACE_USB_HUB = new Guid("F18A0E88-C30C-11D0-8815-00A0C906BED8"); public static Guid GUID_DEVINTERFACE_WPD = new Guid("6AC27878-A6FA-4155-BA85-F98F491D4F33"); public static Guid GUID_DEVINTERFACE_WPD_PRIVATE = new Guid("BA0C718F-4DED-49B7-BDD3-FABE28661211"); public static Guid GUID_DEVINTERFACE_SIDESHOW = new Guid("152E5811-FEB9-4B00-90F4-D32947AE1681"); } [StructLayout(LayoutKind.Sequential)] public struct DISK_EXTENT { internal uint DiskNumber; internal ulong StartingOffset; internal ulong ExtentLength; } [StructLayout(LayoutKind.Sequential)] public class VOLUME_DISK_EXTENTS { public uint NumberOfDiskExtents; public uint ZBUf; } [StructLayout(LayoutKind.Sequential)] public class DEV_BROADCAST_DEVICEINTERFACE { public int dbcc_devicetype; public int dbcc_reserved; public int dbcc_size; } [StructLayout(LayoutKind.Sequential)] public struct SP_DEVINFO_DATA { public uint cbSize; public Guid classGuid; public uint devInst; public UIntPtr reserved; } [StructLayout(LayoutKind.Sequential)] public class SP_DEVINSTALL_PARAMS { public int cbSize; public IntPtr ClassInstallReserved; [MarshalAs(UnmanagedType.LPTStr)] public string DriverPath; public IntPtr FileQueue; public int Flags; public int FlagsEx; public IntPtr hwndParent; public IntPtr InstallMsgHandler; public IntPtr InstallMsgHandlerContext; public int Reserved; } [StructLayout(LayoutKind.Sequential)] public class SP_PROPCHANGE_PARAMS { public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER(); public int HwProfile; public int Scope; public int StateChange; } [StructLayout(LayoutKind.Sequential)] public class SP_CLASSINSTALL_HEADER { public int cbSize; public int InstallFunction; } [StructLayout(LayoutKind.Sequential)] public struct SP_DEVICE_INTERFACE_DETAIL_DATA { public uint cbSize; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string devicePath; } [StructLayout(LayoutKind.Sequential)] public struct SP_DEVICE_INTERFACE_DATA { public uint cbSize; public uint flags; public Guid interfaceClassGuid; public UIntPtr reserved; } }
using System;
using System.Runtime.InteropServices;
using System.Text;

/// <summary>
///     Contains some native Windows and SetupDi API calls
/// </summary>
public class NativeWin32
{
    [Flags]
    public enum DeviceCapabilities
    {
        Unknown           = 0x00000000,
        LockSupported     = 0x00000001,
        EjectSupported    = 0x00000002,
        Removable         = 0x00000004,
        DockDevice        = 0x00000008,
        UniqueId          = 0x00000010,
        SilentInstall     = 0x00000020,
        RawDeviceOk       = 0x00000040,
        SurpriseRemovalOk = 0x00000080,
        HardwareDisabled  = 0x00000100,
        NonDynamic        = 0x00000200
    }

    [Flags]
    public enum DICFG
    {
        /// <summary>
        ///     Return only the device that is associated with the system default device interface, if one is set.
        /// </summary>
        DEFAULT = 0x00000001,

        /// <summary>
        ///     Return only devices that are currently present in a system.
        /// </summary>
        PRESENT = 0x00000002,

        /// <summary>
        ///     Return a list of installed devices for all device setup classes or all device interface classes.
        /// </summary>
        ALLCLASSES = 0x00000004,

        /// <summary>
        ///     Return only devices that are a part of the current hardware profile.
        /// </summary>
        PROFILE = 0x00000008,

        /// <summary>
        ///     Return devices that support device interfaces for the specified device interface classes.
        /// </summary>
        DEVICEINTERFACE = 0x00000010
    }

    public enum PNP_VETO_TYPE
    {
        Ok,
        TypeUnknown,
        LegacyDevice,
        PendingClose,
        WindowsApp,
        WindowsService,
        OutstandingOpen,
        Device,
        Driver,
        IllegalDeviceRequest,
        InsufficientPower,
        NonDisableable,
        LegacyDriver
    }

    /// <summary>
    ///     SEE: https://msdn.microsoft.com/en-us/library/windows/hardware/ff542548(v=vs.85).aspx
    /// </summary>
    [Flags]
    public enum SPDRP
    {
        /// <summary>
        ///     Requests a string describing the device, such as "Microsoft PS/2 Port Mouse", typically defined by the
        ///     manufacturer.
        ///     String
        /// </summary>
        DeviceDesc = 0x00000000,

        /// <summary>
        ///     Requests the hardware IDs provided by the device that identify the device.
        ///     String[]
        /// </summary>
        HardwareId = 0x00000001,

        /// <summary>
        ///     Requests the compatible IDs reported by the device.
        ///     string[]
        /// </summary>
        CompatibleIds = 0x00000002,
        Unused0 = 0x00000003,

        /// <summary>
        ///     Service device property represents the name of the service that is installed for a device instance.
        ///     string
        /// </summary>
        Service = 0x00000004,
        Unused1 = 0x00000005,
        Unused2 = 0x00000006,

        /// <summary>
        ///     Requests the name of the device's setup class, in text format.
        ///     string
        /// </summary>
        Class = 0x00000007,

        /// <summary>
        ///     Requests the GUID for the device's setup class.
        ///     GUID
        /// </summary>
        ClassGuid = 0x00000008,

        /// <summary>
        ///     Requests the name of the driver-specific registry key.
        ///     string
        /// </summary>
        Driver = 0x00000009,

        /// <summary>
        ///     ConfigFlags device property represents the configuration flags that are set for a device instance.
        ///     Int32
        /// </summary>
        ConfigFlags = 0x0000000A,

        /// <summary>
        ///     Requests a string identifying the manufacturer of the device.
        ///     string
        /// </summary>
        Mfg = 0x0000000B,

        /// <summary>
        ///     Requests a string that can be used to distinguish between two similar devices, typically defined by the class
        ///     installer.
        ///     string
        /// </summary>
        FriendlyName = 0x0000000C,

        /// <summary>
        ///     Requests information about the device's location on the bus; the interpretation of this information is
        ///     bus-specific.
        ///     string
        /// </summary>
        LocationInformation = 0x0000000D,

        /// <summary>
        ///     Requests the name of the PDO for this device.
        ///     Binary
        /// </summary>
        PhysicalDeviceObjectName = 0x0000000E,

        /// <summary>
        ///     Capabilities device property represents the capabilities of a device instance.
        ///     Int32
        /// </summary>
        Capabilities = 0x0000000F,

        /// <summary>
        ///     Requests a number associated with the device that can be displayed in the user interface.
        ///     Int32
        /// </summary>
        UiNumber = 0x00000010,

        /// <summary>
        ///     UpperFilters device property represents a list of the service names of the upper-level filter drivers that are
        ///     installed for a device instance.
        ///     string[]
        /// </summary>
        UpperFilters = 0x00000011,

        /// <summary>
        ///     LowerFilters device property represents a list of the service names of the lower-level filter drivers that are
        ///     installed for a device instance.
        ///     string[]
        /// </summary>
        LowerFilters = 0x00000012,

        /// <summary>
        ///     Requests the GUID for the bus that the device is connected to.
        ///     GUID
        /// </summary>
        BusTypeGuid = 0x00000013,

        /// <summary>
        ///     Requests the bus type, such as PCIBus or PCMCIABus.
        ///     Int32
        /// </summary>
        LegacyBusType = 0x00000014,

        /// <summary>
        ///     Requests the legacy bus number of the bus the device is connected to.
        ///     Int32
        /// </summary>
        BusNumber = 0x00000015,

        /// <summary>
        ///     Requests the name of the enumerator for the device, such as "USB".
        ///     string
        /// </summary>
        EnumeratorName = 0x00000016,

        /// <summary>
        ///     Security device property represents a security descriptor structure for a device instance.
        ///     SECURITY_DESCRIPTOR
        /// </summary>
        Security = 0x00000017,

        /// <summary>
        ///     SecuritySDS device property represents a security descriptor string for a device instance.
        /// </summary>
        Security_SDS = 0x00000018,

        /// <summary>
        ///     DevType device property represents the device type of a device instance.
        ///     Int32
        /// </summary>
        DevType = 0x00000019,

        /// <summary>
        ///     Exclusive device property represents a Boolean value that determines whether a device instance can be opened for
        ///     exclusive use.
        ///     bool
        /// </summary>
        Exclusive = 0x0000001A,

        /// <summary>
        ///     Characteristics device property represents the characteristics of a device instance.
        ///     INt32
        /// </summary>
        Characteristics = 0x0000001B,

        /// <summary>
        ///     Requests the address of the device on the bus.
        ///     Int32
        /// </summary>
        Address = 0x0000001C,

        /// <summary>
        ///     UINumberDescFormat device property represents a printf-compatible format string that you should use to display
        ///     the value of the DEVPKEY_DEVICE_UINumber device property for a device instance.
        ///     string
        /// </summary>
        UI_Number_Desc_Format = 0x0000001D,

        /// <summary>
        ///     PowerData device property represents power information about a device instance.
        ///     Binary
        /// </summary>
        Device_Power_Data = 0x0000001E,

        /// <summary>
        ///     (Windows XP and later.) Requests the device's current removal policy. The operating system uses this value as a
        ///     hint to determine how the device is normally removed.
        ///     Int32
        /// </summary>
        RemovalPolicy = 0x0000001F,

        /// <summary>
        ///     RemovalPolicyDefault device property represents the default removal policy for a device instance.
        ///     Int32
        /// </summary>
        Removal_Policy_Defualt = 0x00000020,

        /// <summary>
        ///     RemovalPolicyOverride device property represents the removal policy override for a device instance.
        ///     Int32
        /// </summary>
        Removal_Policy_Override = 0x00000021,

        /// <summary>
        ///     Windows XP and later.) Requests the device's installation state.
        ///     Int32
        /// </summary>
        InstallState = 0x00000022,

        /// <summary>
        ///     LocationPaths device property represents the location of a device instance in the device tree.
        ///     string[]
        /// </summary>
        LocationPaths = 0x00000023
    }

    public const int INVALID_HANDLE_VALUE                = -1;
    public const int MAX_DEV_LEN                         = 200;
    public const int DEVICE_NOTIFY_WINDOW_HANDLE         = 0x00000000;
    public const int DEVICE_NOTIFY_SERVICE_HANDLE        = 0x00000001;
    public const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004;
    public const int DBT_DEVTYP_DEVICEINTERFACE          = 0x00000005;
    public const int DBT_DEVNODES_CHANGED                = 0x0007;
    public const int WM_DEVICECHANGE                     = 0x0219;
    public const int DIF_PROPERTYCHANGE                  = 0x00000012;
    public const int DICS_FLAG_GLOBAL                    = 0x00000001;
    public const int DICS_FLAG_CONFIGSPECIFIC            = 0x00000002;
    public const int DICS_ENABLE                         = 0x00000001;
    public const int DICS_DISABLE                        = 0x00000002;

    public const int ERROR_INVALID_DATA                   = 13;
    public const int ERROR_NO_MORE_ITEMS                  = 259;
    public const int ERROR_INSUFFICIENT_BUFFER            = 122;
    public const int GENERIC_READ                         = unchecked((int) 0x80000000);
    public const int FILE_SHARE_READ                      = 0x00000001;
    public const int FILE_SHARE_WRITE                     = 0x00000002;
    public const int OPEN_EXISTING                        = 3;
    public const int IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000;

    [DllImport("setupapi.dll")]
    public static extern bool SetupDiOpenDeviceInfo(
        IntPtr              deviceInfoSet,
        string              deviceInstanceId,
        IntPtr              hwndParent,
        int                 openFlags,
        ref SP_DEVINFO_DATA deviceInfoData
    );

    [DllImport("Kernel32.dll", SetLastError = true)]
    internal static extern IntPtr CreateFile(string lpFileName,           int dwDesiredAccess,       int dwShareMode,
        IntPtr                                      lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

    [DllImport("Kernel32.dll", SetLastError = true)]
    internal static extern bool DeviceIoControl(IntPtr hDevice,       int    dwIoControlCode, IntPtr lpInBuffer,
        int                                            nInBufferSize, IntPtr lpOutBuffer,     int    nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);

    [DllImport("Kernel32.dll", SetLastError = true)]
    internal static extern bool CloseHandle(IntPtr hObject);

    [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool GetVolumeNameForVolumeMountPoint(
        string        volumeName,
        StringBuilder uniqueVolumeName,
        int           uniqueNameBufferCapacity);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient,
        DEV_BROADCAST_DEVICEINTERFACE                             NotificationFilter, uint Flags);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern uint UnregisterDeviceNotification(IntPtr hHandle);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern IntPtr SetupDiGetClassDevs(ref Guid   ClassGuid,
        [MarshalAs(UnmanagedType.LPStr)]                string Enumerator, IntPtr hwndParent, DICFG Flags);

    [DllImport("setupapi.dll")]
    public static extern IntPtr SetupDiGetClassDevsEx(ref Guid   ClassGuid,
        [MarshalAs(UnmanagedType.LPStr)]                  string Enumerator,
        IntPtr                                                   hwndParent, int Flags, IntPtr DeviceInfoSet,
        [MarshalAs(UnmanagedType.LPStr)] string                  MachineName,
        IntPtr                                                   Reserved);

    [DllImport(@"setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiEnumDeviceInterfaces(
        IntPtr                       hDevInfo,
        IntPtr                       devInfo,
        Guid                         interfaceClassGuid,
        uint                         memberIndex,
        ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
    );

    [DllImport(@"setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiEnumDeviceInterfaces(
        IntPtr                       hDevInfo,
        ref SP_DEVINFO_DATA          devInfo,
        Guid                         interfaceClassGuid,
        uint                         memberIndex,
        ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData
    );

    [DllImport(@"setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiGetDeviceInterfaceDetail(
        IntPtr                              hDevInfo,
        ref SP_DEVICE_INTERFACE_DATA        deviceInterfaceData,
        ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
        uint                                deviceInterfaceDetailDataSize,
        ref uint                            requiredSize,
        ref SP_DEVINFO_DATA                 deviceInfoData
    );

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiEnumDeviceInfo(IntPtr lpInfoSet, int dwIndex, ref SP_DEVINFO_DATA devInfoData);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
        uint                                                          Property,
        uint                                                          PropertyRegDataType, StringBuilder PropertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr lpInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
        uint                                                          Property,
        uint                                                          PropertyRegDataType, IntPtr propertyBuffer, uint PropertyBufferSize, IntPtr RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool SetupDiSetClassInstallParams(IntPtr DeviceInfoSet,      ref SP_DEVINFO_DATA DeviceInfoData,
        SP_PROPCHANGE_PARAMS                                      ClassInstallParams, int                 ClassInstallParamsSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupDiCallClassInstaller(uint InstallFunction, IntPtr DeviceInfoSet,
        ref SP_DEVINFO_DATA                                  DeviceInfoData);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupDiClassNameFromGuid(ref Guid ClassGuid,     StringBuilder className,
        int                                                     ClassNameSize, ref int       RequiredSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupDiGetClassDescription(ref Guid ClassGuid,            StringBuilder classDescription,
        int                                                       ClassDescriptionSize, ref int       RequiredSize);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet,    ref SP_DEVINFO_DATA DeviceInfoData,
        StringBuilder                                           DeviceInstanceId, int                 DeviceInstanceIdSize, ref int RequiredSize);

    [DllImport("setupapi.dll")]
    public static extern int CM_Get_Parent(
        out uint pdnDevInst,
        uint     dnDevInst,
        int      ulFlags
    );

    [DllImport("setupapi.dll")]
    public static extern int CM_Get_Device_ID(
        uint          dnDevInst,
        StringBuilder buffer,
        uint          bufferLen,
        int           ulFlags);

    [DllImport("setupapi.dll")]
    public static extern int CM_Request_Device_Eject(
        uint              dnDevInst,
        out PNP_VETO_TYPE pVetoType,
        StringBuilder     pszVetoName,
        int               ulNameLength,
        uint              ulFlags
    );

    [DllImport("setupapi.dll", EntryPoint = "CM_Request_Device_Eject")]
    public static extern int CM_Request_Device_Eject_NoUi(
        uint          dnDevInst,
        IntPtr        pVetoType,
        StringBuilder pszVetoName,
        uint          ulNameLength,
        uint          ulFlags
    );

    public class GUID_DEVINTERFACE
    {
        public static Guid BUS1394_CLASS_GUID                   = new Guid("6BDD1FC1-810F-11d0-BEC7-08002BE2092F");
        public static Guid GUID_61883_CLASS                     = new Guid("7EBEFBC0-3200-11d2-B4C2-00A0C9697D07");
        public static Guid GUID_DEVICE_APPLICATIONLAUNCH_BUTTON = new Guid("629758EE-986E-4D9E-8E47-DE27F8AB054D");
        public static Guid GUID_DEVICE_BATTERY                  = new Guid("72631E54-78A4-11D0-BCF7-00AA00B7B32A");
        public static Guid GUID_DEVICE_LID                      = new Guid("4AFA3D52-74A7-11d0-be5e-00A0C9062857");
        public static Guid GUID_DEVICE_MEMORY                   = new Guid("3FD0F03D-92E0-45FB-B75C-5ED8FFB01021");
        public static Guid GUID_DEVICE_MESSAGE_INDICATOR        = new Guid("CD48A365-FA94-4CE2-A232-A1B764E5D8B4");
        public static Guid GUID_DEVICE_PROCESSOR                = new Guid("97FADB10-4E33-40AE-359C-8BEF029DBDD0");
        public static Guid GUID_DEVICE_SYS_BUTTON               = new Guid("4AFA3D53-74A7-11d0-be5e-00A0C9062857");
        public static Guid GUID_DEVICE_THERMAL_ZONE             = new Guid("4AFA3D51-74A7-11d0-be5e-00A0C9062857");
        public static Guid GUID_BTHPORT_DEVICE_INTERFACE        = new Guid("0850302A-B344-4fda-9BE9-90576B8D46F0");
        public static Guid GUID_DEVINTERFACE_BRIGHTNESS         = new Guid("FDE5BBA4-B3F9-46FB-BDAA-0728CE3100B4");
        public static Guid GUID_DEVINTERFACE_DISPLAY_ADAPTER    = new Guid("5B45201D-F2F2-4F3B-85BB-30FF1F953599");
        public static Guid GUID_DEVINTERFACE_I2C                = new Guid("2564AA4F-DDDB-4495-B497-6AD4A84163D7");
        public static Guid GUID_DEVINTERFACE_IMAGE              = new Guid("6BDD1FC6-810F-11D0-BEC7-08002BE2092F");
        public static Guid GUID_DEVINTERFACE_MONITOR            = new Guid("E6F07B5F-EE97-4a90-B076-33F57BF4EAA7");
        public static Guid GUID_DEVINTERFACE_OPM                = new Guid("BF4672DE-6B4E-4BE4-A325-68A91EA49C09");

        public static Guid GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL =
            new Guid("1AD9E4F0-F88D-4360-BAB9-4C2D55E564CD");

        public static Guid GUID_DISPLAY_DEVICE_ARRIVAL = new Guid("1CA05180-A699-450A-9A0C-DE4FBE3DDD89");
        public static Guid GUID_DEVINTERFACE_HID       = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030");
        public static Guid GUID_DEVINTERFACE_KEYBOARD  = new Guid("884b96c3-56ef-11d1-bc8c-00a0c91405dd");
        public static Guid GUID_DEVINTERFACE_MOUSE     = new Guid("378DE44C-56EF-11D1-BC8C-00A0C91405DD");
        public static Guid GUID_DEVINTERFACE_MODEM     = new Guid("2C7089AA-2E0E-11D1-B114-00C04FC2AAE4");
        public static Guid GUID_DEVINTERFACE_NET       = new Guid("CAC88484-7515-4C03-82E6-71A87ABAC361");

        public static Guid GUID_DEVINTERFACE_SENSOR = new Guid(0XBA1BB692, 0X9B7A, 0X4833, 0X9A, 0X1E, 0X52, 0X5E,
            0XD1, 0X34, 0XE7, 0XE2);

        public static Guid GUID_DEVINTERFACE_COMPORT  = new Guid("86E0D1E0-8089-11D0-9CE4-08003E301F73");
        public static Guid GUID_DEVINTERFACE_PARALLEL = new Guid("97F76EF0-F883-11D0-AF1F-0000F800845C");
        public static Guid GUID_DEVINTERFACE_PARCLASS = new Guid("811FC6A5-F728-11D0-A537-0000F8753ED1");

        public static Guid GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR =
            new Guid("4D36E978-E325-11CE-BFC1-08002BE10318");

        public static Guid GUID_DEVINTERFACE_CDCHANGER           = new Guid("53F56312-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_CDROM               = new Guid("53F56308-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_DISK                = new Guid("53F56307-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_FLOPPY              = new Guid("53F56311-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_MEDIUMCHANGER       = new Guid("53F56310-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_PARTITION           = new Guid("53F5630A-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_STORAGEPORT         = new Guid("2ACCFE60-C130-11D2-B082-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_TAPE                = new Guid("53F5630B-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_VOLUME              = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_DEVINTERFACE_WRITEONCEDISK       = new Guid("53F5630C-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_IO_VOLUME_DEVICE_INTERFACE       = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid MOUNTDEV_MOUNTED_DEVICE_GUID          = new Guid("53F5630D-B6BF-11D0-94F2-00A0C91EFB8B");
        public static Guid GUID_AVC_CLASS                        = new Guid("095780C3-48A1-4570-BD95-46707F78C2DC");
        public static Guid GUID_VIRTUAL_AVC_CLASS                = new Guid("616EF4D0-23CE-446D-A568-C31EB01913D0");
        public static Guid KSCATEGORY_ACOUSTIC_ECHO_CANCEL       = new Guid("BF963D80-C559-11D0-8A2B-00A0C9255AC1");
        public static Guid KSCATEGORY_AUDIO                      = new Guid("6994AD04-93EF-11D0-A3CC-00A0C9223196");
        public static Guid KSCATEGORY_AUDIO_DEVICE               = new Guid("FBF6F530-07B9-11D2-A71E-0000F8004788");
        public static Guid KSCATEGORY_AUDIO_GFX                  = new Guid("9BAF9572-340C-11D3-ABDC-00A0C90AB16F");
        public static Guid KSCATEGORY_AUDIO_SPLITTER             = new Guid("9EA331FA-B91B-45F8-9285-BD2BC77AFCDE");
        public static Guid KSCATEGORY_BDA_IP_SINK                = new Guid("71985F4A-1CA1-11d3-9CC8-00C04F7971E0");
        public static Guid KSCATEGORY_BDA_NETWORK_EPG            = new Guid("71985F49-1CA1-11d3-9CC8-00C04F7971E0");
        public static Guid KSCATEGORY_BDA_NETWORK_PROVIDER       = new Guid("71985F4B-1CA1-11d3-9CC8-00C04F7971E0");
        public static Guid KSCATEGORY_BDA_NETWORK_TUNER          = new Guid("71985F48-1CA1-11d3-9CC8-00C04F7971E0");
        public static Guid KSCATEGORY_BDA_RECEIVER_COMPONENT     = new Guid("FD0A5AF4-B41D-11d2-9C95-00C04F7971E0");
        public static Guid KSCATEGORY_BDA_TRANSPORT_INFORMATION  = new Guid("A2E3074F-6C3D-11d3-B653-00C04F79498E");
        public static Guid KSCATEGORY_BRIDGE                     = new Guid("085AFF00-62CE-11CF-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_CAPTURE                    = new Guid("65E8773D-8F56-11D0-A3B9-00A0C9223196");
        public static Guid KSCATEGORY_CLOCK                      = new Guid("53172480-4791-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_COMMUNICATIONSTRANSFORM    = new Guid("CF1DDA2C-9743-11D0-A3EE-00A0C9223196");
        public static Guid KSCATEGORY_CROSSBAR                   = new Guid("A799A801-A46D-11D0-A18C-00A02401DCD4");
        public static Guid KSCATEGORY_DATACOMPRESSOR             = new Guid("1E84C900-7E70-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_DATADECOMPRESSOR           = new Guid("2721AE20-7E70-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_DATATRANSFORM              = new Guid("2EB07EA0-7E70-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_DRM_DESCRAMBLE             = new Guid("FFBB6E3F-CCFE-4D84-90D9-421418B03A8E");
        public static Guid KSCATEGORY_ENCODER                    = new Guid("19689BF6-C384-48fd-AD51-90E58C79F70B");
        public static Guid KSCATEGORY_ESCALANTE_PLATFORM_DRIVER  = new Guid("74F3AEA8-9768-11D1-8E07-00A0C95EC22E");
        public static Guid KSCATEGORY_FILESYSTEM                 = new Guid("760FED5E-9357-11D0-A3CC-00A0C9223196");
        public static Guid KSCATEGORY_INTERFACETRANSFORM         = new Guid("CF1DDA2D-9743-11D0-A3EE-00A0C9223196");
        public static Guid KSCATEGORY_MEDIUMTRANSFORM            = new Guid("CF1DDA2E-9743-11D0-A3EE-00A0C9223196");
        public static Guid KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR = new Guid("830A44F2-A32D-476B-BE97-42845673B35A");
        public static Guid KSCATEGORY_MIXER                      = new Guid("AD809C00-7B88-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_MULTIPLEXER                = new Guid("7A5DE1D3-01A1-452c-B481-4FA2B96271E8");
        public static Guid KSCATEGORY_NETWORK                    = new Guid("67C9CC3C-69C4-11D2-8759-00A0C9223196");
        public static Guid KSCATEGORY_PREFERRED_MIDIOUT_DEVICE   = new Guid("D6C50674-72C1-11D2-9755-0000F8004788");
        public static Guid KSCATEGORY_PREFERRED_WAVEIN_DEVICE    = new Guid("D6C50671-72C1-11D2-9755-0000F8004788");
        public static Guid KSCATEGORY_PREFERRED_WAVEOUT_DEVICE   = new Guid("D6C5066E-72C1-11D2-9755-0000F8004788");
        public static Guid KSCATEGORY_PROXY                      = new Guid("97EBAACA-95BD-11D0-A3EA-00A0C9223196");
        public static Guid KSCATEGORY_QUALITY                    = new Guid("97EBAACB-95BD-11D0-A3EA-00A0C9223196");
        public static Guid KSCATEGORY_REALTIME                   = new Guid("EB115FFC-10C8-4964-831D-6DCB02E6F23F");
        public static Guid KSCATEGORY_RENDER                     = new Guid("65E8773E-8F56-11D0-A3B9-00A0C9223196");
        public static Guid KSCATEGORY_SPLITTER                   = new Guid("0A4252A0-7E70-11D0-A5D6-28DB04C10000");
        public static Guid KSCATEGORY_SYNTHESIZER                = new Guid("DFF220F3-F70F-11D0-B917-00A0C9223196");
        public static Guid KSCATEGORY_SYSAUDIO                   = new Guid("A7C7A5B1-5AF3-11D1-9CED-00A024BF0407");
        public static Guid KSCATEGORY_TEXT                       = new Guid("6994AD06-93EF-11D0-A3CC-00A0C9223196");
        public static Guid KSCATEGORY_TOPOLOGY                   = new Guid("DDA54A40-1E4C-11D1-A050-405705C10000");
        public static Guid KSCATEGORY_TVAUDIO                    = new Guid("A799A802-A46D-11D0-A18C-00A02401DCD4");
        public static Guid KSCATEGORY_TVTUNER                    = new Guid("A799A800-A46D-11D0-A18C-00A02401DCD4");
        public static Guid KSCATEGORY_VBICODEC                   = new Guid("07DAD660-22F1-11D1-A9F4-00C04FBBDE8F");
        public static Guid KSCATEGORY_VIDEO                      = new Guid("6994AD05-93EF-11D0-A3CC-00A0C9223196");
        public static Guid KSCATEGORY_VIRTUAL                    = new Guid("3503EAC4-1F26-11D1-8AB0-00A0C9223196");
        public static Guid KSCATEGORY_VPMUX                      = new Guid("A799A803-A46D-11D0-A18C-00A02401DCD4");
        public static Guid KSCATEGORY_WDMAUD                     = new Guid("3E227E76-690D-11D2-8161-0000F8775BF1");
        public static Guid KSMFT_CATEGORY_AUDIO_DECODER          = new Guid("9ea73fb4-ef7a-4559-8d5d-719d8f0426c7");
        public static Guid KSMFT_CATEGORY_AUDIO_EFFECT           = new Guid("11064c48-3648-4ed0-932e-05ce8ac811b7");
        public static Guid KSMFT_CATEGORY_AUDIO_ENCODER          = new Guid("91c64bd0-f91e-4d8c-9276-db248279d975");
        public static Guid KSMFT_CATEGORY_DEMULTIPLEXER          = new Guid("a8700a7a-939b-44c5-99d7-76226b23b3f1");
        public static Guid KSMFT_CATEGORY_MULTIPLEXER            = new Guid("059c561e-05ae-4b61-b69d-55b61ee54a7b");
        public static Guid KSMFT_CATEGORY_OTHER                  = new Guid("90175d57-b7ea-4901-aeb3-933a8747756f");
        public static Guid KSMFT_CATEGORY_VIDEO_DECODER          = new Guid("d6c02d4b-6833-45b4-971a-05a4b04bab91");
        public static Guid KSMFT_CATEGORY_VIDEO_EFFECT           = new Guid("12e17c21-532c-4a6e-8a1c-40825a736397");
        public static Guid KSMFT_CATEGORY_VIDEO_ENCODER          = new Guid("f79eac7d-e545-4387-bdee-d647d7bde42a");
        public static Guid KSMFT_CATEGORY_VIDEO_PROCESSOR        = new Guid("302ea3fc-aa5f-47f9-9f7a-c2188bb16302");
        public static Guid GUID_DEVINTERFACE_USB_DEVICE          = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED");
        public static Guid GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new Guid("3ABF6F2D-71C4-462A-8A92-1E6861E6AF27");
        public static Guid GUID_DEVINTERFACE_USB_HUB             = new Guid("F18A0E88-C30C-11D0-8815-00A0C906BED8");
        public static Guid GUID_DEVINTERFACE_WPD                 = new Guid("6AC27878-A6FA-4155-BA85-F98F491D4F33");
        public static Guid GUID_DEVINTERFACE_WPD_PRIVATE         = new Guid("BA0C718F-4DED-49B7-BDD3-FABE28661211");
        public static Guid GUID_DEVINTERFACE_SIDESHOW            = new Guid("152E5811-FEB9-4B00-90F4-D32947AE1681");
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DISK_EXTENT
    {
        internal uint  DiskNumber;
        internal ulong StartingOffset;
        internal ulong ExtentLength;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class VOLUME_DISK_EXTENTS
    {
        public uint NumberOfDiskExtents;

        public uint ZBUf;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class DEV_BROADCAST_DEVICEINTERFACE
    {
        public int dbcc_devicetype;
        public int dbcc_reserved;
        public int dbcc_size;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVINFO_DATA
    {
        public uint    cbSize;
        public Guid    classGuid;
        public uint    devInst;
        public UIntPtr reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class SP_DEVINSTALL_PARAMS
    {
        public int    cbSize;
        public IntPtr ClassInstallReserved;

        [MarshalAs(UnmanagedType.LPTStr)] public string DriverPath;

        public IntPtr FileQueue;
        public int    Flags;
        public int    FlagsEx;
        public IntPtr hwndParent;
        public IntPtr InstallMsgHandler;
        public IntPtr InstallMsgHandlerContext;
        public int    Reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class SP_PROPCHANGE_PARAMS
    {
        public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
        public int                    HwProfile;
        public int                    Scope;
        public int                    StateChange;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class SP_CLASSINSTALL_HEADER
    {
        public int cbSize;
        public int InstallFunction;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        public uint cbSize;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string devicePath;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVICE_INTERFACE_DATA
    {
        public uint    cbSize;
        public uint    flags;
        public Guid    interfaceClassGuid;
        public UIntPtr reserved;
    }
}

SetupDi.cs

Gather Information from the Device Setup or Information Class

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
public class SetupDi
{
/// <summary>
/// Class Names (Incomplete??)
/// </summary>
public string[] ClassNames =
{
"AudioEndpoint",
"CDROM",
"Computer",
"DiskDrive",
"Display",
"DriverInterface",
"HDC",
"HIDClass",
"Keyboard",
"MEDIA",
"Monitor",
"Mouse",
"Net",
"PrintQueue",
"Processor",
"SCSIAdapter",
"Sensor",
"SoftwareDevice",
"System",
"USB",
"Volume",
"VolumeSnapshot",
"WPD"
};
/// <summary>
/// Are we running using 64 or 32 bits
/// </summary>
public bool Is64Bit => IntPtr.Size == 8;
/// <summary>
/// 32 and 64 bit operations require differing offsets within the pointer return values
/// </summary>
public uint OffsetSize => Is64Bit ? 8u : 6u;
/// <summary>
/// Get a list of active drives on the PC.
/// </summary>
public IEnumerable<string> GetDriveReadyList => from driveInfo in DriveInfo.GetDrives() where driveInfo.IsReady select driveInfo.Name;
/// <summary>
/// Get a list of drive: letters and associated MountPoints for active drives.
/// </summary>
public Dictionary<string, string> LogicalDrives
{
get
{
var _logicalDrives = new Dictionary<string, string>();
foreach (var drive in GetDriveReadyList)
{
var sb = new StringBuilder(1024);
if (NativeWin32.GetVolumeNameForVolumeMountPoint(drive, sb, sb.Capacity))
_logicalDrives.Add(drive.Replace("\\", ""), sb.ToString());
}
return _logicalDrives;
}
}
/// <summary>
/// Get a list of Drive: -> Disk numbers and associated length and offset information
/// </summary>
public Dictionary<string, List<NativeWin32.DISK_EXTENT>> DiskNumbers
{
get
{
var _diskNumbers = new Dictionary<string, List<NativeWin32.DISK_EXTENT>>();
foreach (var ld in LogicalDrives)
{
var dkexts = new List<NativeWin32.DISK_EXTENT>();
var hFile = NativeWin32.CreateFile(@"\\.\" + ld.Key, NativeWin32.GENERIC_READ, NativeWin32.FILE_SHARE_READ | NativeWin32.FILE_SHARE_WRITE, IntPtr.Zero, NativeWin32.OPEN_EXISTING, 0, IntPtr.Zero);
if (hFile == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
throw new Win32Exception(Marshal.GetLastWin32Error());
var size = 1024;
var buffer = Marshal.AllocHGlobal(size);
var alloced = buffer;
var bytesReturned = 0;
try
{
if (!NativeWin32.DeviceIoControl(hFile, NativeWin32.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, buffer, size, out bytesReturned, IntPtr.Zero))
{
}
}
catch (Exception e)
{
var m = e.Message;
}
finally
{
NativeWin32.CloseHandle(hFile);
}
if (bytesReturned > 0)
{
var vde = new NativeWin32.VOLUME_DISK_EXTENTS();
var dextent = new NativeWin32.DISK_EXTENT();
Marshal.PtrToStructure(buffer, vde);
buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(vde));
for (var i = 0; i < vde.NumberOfDiskExtents; i++)
{
dextent = (NativeWin32.DISK_EXTENT) Marshal.PtrToStructure(buffer, typeof(NativeWin32.DISK_EXTENT));
dkexts.Add(dextent);
buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(dextent));
}
}
Marshal.FreeHGlobal(alloced);
_diskNumbers.Add(ld.Key, dkexts);
}
return _diskNumbers;
}
}
/// <summary>
/// Associates a drive letter to its physical disk numbers using SetupDi API functions.
/// </summary>
/// <returns></returns>
public Dictionary<string, VOLUME_INFO> GetVolumePaths()
{
var _volumepaths = new Dictionary<string, VOLUME_INFO>();
var classGuid = NativeWin32.GUID_DEVINTERFACE.GUID_DEVINTERFACE_VOLUME;
var hDevInfo = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.DEVICEINTERFACE | NativeWin32.DICFG.PRESENT);
if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
throw new Exception("read hardware information error");
var devIndex = 0u;
do
{
var dia = new NativeWin32.SP_DEVICE_INTERFACE_DATA();
dia.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVICE_INTERFACE_DATA));
if (NativeWin32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, classGuid, devIndex, ref dia))
{
var didd = new NativeWin32.SP_DEVICE_INTERFACE_DETAIL_DATA();
didd.cbSize = OffsetSize;
var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
uint nRequiredSize = 0;
uint nBytes = 1024;
if (NativeWin32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref dia, ref didd, nBytes, ref nRequiredSize, ref devInfoData))
{
var sb = new StringBuilder(1024);
if (NativeWin32.GetVolumeNameForVolumeMountPoint(didd.devicePath + @"\", sb, sb.Capacity))
{
var cv = sb.ToString();
var di = new VOLUME_INFO();
foreach (var V in LogicalDrives)
if (V.Value.IndexOf(cv, StringComparison.OrdinalIgnoreCase) != -1)
{
di.Drive = V.Key;
di.VolumeMountPoint = cv;
di.DevicePath = didd.devicePath;
foreach (var de in DiskNumbers[V.Key])
{
di.DiskNumbers.Add(de.DiskNumber);
di.ExtentLengths.Add(de.ExtentLength);
di.StartingOffsets.Add(de.StartingOffset);
}
_volumepaths.Add(di.Drive.Trim(), di);
break;
}
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
break;
}
devIndex++;
} while (true);
NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
return _volumepaths;
}
/// <summary>
/// Retrieves a list of All devices installed starting at the base root
/// </summary>
public Dictionary<string, DEVICE_INFO> GetAllGUIDs()
{
var DevList = new Dictionary<string, DEVICE_INFO>();
var classGuid = Guid.Empty;
var hDevInfo = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.ALLCLASSES | NativeWin32.DICFG.PRESENT);
if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
throw new Exception("read hardware information error");
var devIndex = 0;
do
{
var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
devInfoData.classGuid = Guid.Empty;
devInfoData.devInst = 0;
devInfoData.reserved = UIntPtr.Zero;
if (!NativeWin32.SetupDiEnumDeviceInfo(hDevInfo, devIndex, ref devInfoData))
break;
var di = new DEVICE_INFO();
di.Guid = $"{devInfoData.classGuid}";
di.ClassName = $"{GetClassNameFromGuid(devInfoData.classGuid)}";
di.Description = $"{GetClassDescriptionFromGuid(devInfoData.classGuid)}";
di.InstanceID = $"{GetDeviceInstanceId(hDevInfo, devInfoData)}";
di.DI_Capabilities = (NativeWin32.DeviceCapabilities) GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Capabilities, 0);
di.FriendlyName = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.FriendlyName, null);
di.ClassGuid = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.ClassGuid, null);
di.HardwareId = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.HardwareId, null);
di.Mfg = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Mfg, null);
di.EnumeratorName = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.EnumeratorName, null);
if (!DevList.ContainsValue(di))
DevList.Add($"{devIndex}", di);
devIndex++;
} while (true);
NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
return DevList;
}
/// <summary>
/// Get a list of all devices starting a a specified Guid base.
/// </summary>
public IEnumerable<DEVICE_INFO> GetGUIDGroup(string ClassName)
{
if (ClassNames.All(c => c.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) == -1))
throw new Exception("ClassName is Invalid.");
var dl = GetAllGUIDs();
return dl.Values.Where(d => d.ClassName.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) != -1);
}
/// <summary>
/// property is SPDRP type
/// </summary>
private static uint GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, uint defaultValue)
{
var propertyRegDataType = 0u;
var requiredSize = IntPtr.Zero;
var propertyBufferSize = (uint) Marshal.SizeOf(typeof(uint));
var propertyBuffer = Marshal.AllocHGlobal((int) propertyBufferSize);
if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
{
var error = Marshal.GetLastWin32Error();
if (error != NativeWin32.ERROR_INVALID_DATA)
throw new Win32Exception(error);
return defaultValue;
}
var value = (uint) Marshal.PtrToStructure(propertyBuffer, typeof(uint));
Marshal.FreeHGlobal(propertyBuffer);
return value;
}
/// <summary>
/// property is SPDRP type
/// </summary>
private Guid GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devData, uint property, Guid defaultValue)
{
var propertyRegDataType = 0u;
var requiredSize = IntPtr.Zero;
var propertyBufferSize = (uint) Marshal.SizeOf(typeof(Guid));
var propertyBuffer = Marshal.AllocHGlobal((int) propertyBufferSize);
if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
{
Marshal.FreeHGlobal(propertyBuffer);
var error = Marshal.GetLastWin32Error();
if (error != NativeWin32.ERROR_INVALID_DATA)
throw new Win32Exception(error);
return defaultValue;
}
var value = (Guid) Marshal.PtrToStructure(propertyBuffer, typeof(Guid));
Marshal.FreeHGlobal(propertyBuffer);
return value;
}
/// <summary>
/// property is SPDRP type
/// </summary>
private string GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, string defaultValue)
{
var propertyRegDataType = 0u;
var requiredSize = IntPtr.Zero;
var propertyBufferSize = 1024u;
var propertyBuffer = new StringBuilder((int) propertyBufferSize);
if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
{
var error = Marshal.GetLastWin32Error();
if (error != NativeWin32.ERROR_INVALID_DATA)
throw new Win32Exception(error);
return defaultValue;
}
return propertyBuffer.ToString();
}
public string GetClassNameFromGuid(Guid guid)
{
var result = string.Empty;
var className = new StringBuilder();
var iRequiredSize = 0;
var iSize = 0;
var b = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
className = new StringBuilder(iRequiredSize);
iSize = iRequiredSize;
b = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
if (b)
result = className.ToString();
return result;
}
public string GetClassDescriptionFromGuid(Guid guid)
{
var result = string.Empty;
var classDesc = new StringBuilder(0);
var iRequiredSize = 0;
var iSize = 0;
var b = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
classDesc = new StringBuilder(iRequiredSize);
iSize = iRequiredSize;
b = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
if (b)
result = classDesc.ToString();
return result;
}
public string GetDeviceInstanceId(IntPtr DeviceInfoSet, NativeWin32.SP_DEVINFO_DATA DeviceInfoData)
{
var result = string.Empty;
var id = new StringBuilder(0);
var iRequiredSize = 0;
var iSize = 0;
var b = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
id = new StringBuilder(iRequiredSize);
iSize = iRequiredSize;
b = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
if (b)
result = id.ToString();
return result;
}
public class VOLUME_INFO
{
public string DevicePath;
public List<uint> DiskNumbers = new List<uint>();
public string Drive;
public List<ulong> ExtentLengths = new List<ulong>();
public List<ulong> StartingOffsets = new List<ulong>();
public string VolumeMountPoint;
}
public struct DEVICE_INFO
{
public string Guid;
public string ClassName;
public string Description;
public string InstanceID;
public NativeWin32.DeviceCapabilities DI_Capabilities;
public string FriendlyName;
public string ClassGuid;
public string HardwareId;
public string Mfg;
public string EnumeratorName;
public string Drive;
public string VolumePath;
}
}
using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; public class SetupDi { /// <summary> /// Class Names (Incomplete??) /// </summary> public string[] ClassNames = { "AudioEndpoint", "CDROM", "Computer", "DiskDrive", "Display", "DriverInterface", "HDC", "HIDClass", "Keyboard", "MEDIA", "Monitor", "Mouse", "Net", "PrintQueue", "Processor", "SCSIAdapter", "Sensor", "SoftwareDevice", "System", "USB", "Volume", "VolumeSnapshot", "WPD" }; /// <summary> /// Are we running using 64 or 32 bits /// </summary> public bool Is64Bit => IntPtr.Size == 8; /// <summary> /// 32 and 64 bit operations require differing offsets within the pointer return values /// </summary> public uint OffsetSize => Is64Bit ? 8u : 6u; /// <summary> /// Get a list of active drives on the PC. /// </summary> public IEnumerable<string> GetDriveReadyList => from driveInfo in DriveInfo.GetDrives() where driveInfo.IsReady select driveInfo.Name; /// <summary> /// Get a list of drive: letters and associated MountPoints for active drives. /// </summary> public Dictionary<string, string> LogicalDrives { get { var _logicalDrives = new Dictionary<string, string>(); foreach (var drive in GetDriveReadyList) { var sb = new StringBuilder(1024); if (NativeWin32.GetVolumeNameForVolumeMountPoint(drive, sb, sb.Capacity)) _logicalDrives.Add(drive.Replace("\\", ""), sb.ToString()); } return _logicalDrives; } } /// <summary> /// Get a list of Drive: -> Disk numbers and associated length and offset information /// </summary> public Dictionary<string, List<NativeWin32.DISK_EXTENT>> DiskNumbers { get { var _diskNumbers = new Dictionary<string, List<NativeWin32.DISK_EXTENT>>(); foreach (var ld in LogicalDrives) { var dkexts = new List<NativeWin32.DISK_EXTENT>(); var hFile = NativeWin32.CreateFile(@"\\.\" + ld.Key, NativeWin32.GENERIC_READ, NativeWin32.FILE_SHARE_READ | NativeWin32.FILE_SHARE_WRITE, IntPtr.Zero, NativeWin32.OPEN_EXISTING, 0, IntPtr.Zero); if (hFile == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE) throw new Win32Exception(Marshal.GetLastWin32Error()); var size = 1024; var buffer = Marshal.AllocHGlobal(size); var alloced = buffer; var bytesReturned = 0; try { if (!NativeWin32.DeviceIoControl(hFile, NativeWin32.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, buffer, size, out bytesReturned, IntPtr.Zero)) { } } catch (Exception e) { var m = e.Message; } finally { NativeWin32.CloseHandle(hFile); } if (bytesReturned > 0) { var vde = new NativeWin32.VOLUME_DISK_EXTENTS(); var dextent = new NativeWin32.DISK_EXTENT(); Marshal.PtrToStructure(buffer, vde); buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(vde)); for (var i = 0; i < vde.NumberOfDiskExtents; i++) { dextent = (NativeWin32.DISK_EXTENT) Marshal.PtrToStructure(buffer, typeof(NativeWin32.DISK_EXTENT)); dkexts.Add(dextent); buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(dextent)); } } Marshal.FreeHGlobal(alloced); _diskNumbers.Add(ld.Key, dkexts); } return _diskNumbers; } } /// <summary> /// Associates a drive letter to its physical disk numbers using SetupDi API functions. /// </summary> /// <returns></returns> public Dictionary<string, VOLUME_INFO> GetVolumePaths() { var _volumepaths = new Dictionary<string, VOLUME_INFO>(); var classGuid = NativeWin32.GUID_DEVINTERFACE.GUID_DEVINTERFACE_VOLUME; var hDevInfo = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.DEVICEINTERFACE | NativeWin32.DICFG.PRESENT); if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE) throw new Exception("read hardware information error"); var devIndex = 0u; do { var dia = new NativeWin32.SP_DEVICE_INTERFACE_DATA(); dia.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVICE_INTERFACE_DATA)); if (NativeWin32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, classGuid, devIndex, ref dia)) { var didd = new NativeWin32.SP_DEVICE_INTERFACE_DETAIL_DATA(); didd.cbSize = OffsetSize; var devInfoData = new NativeWin32.SP_DEVINFO_DATA(); devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA)); uint nRequiredSize = 0; uint nBytes = 1024; if (NativeWin32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref dia, ref didd, nBytes, ref nRequiredSize, ref devInfoData)) { var sb = new StringBuilder(1024); if (NativeWin32.GetVolumeNameForVolumeMountPoint(didd.devicePath + @"\", sb, sb.Capacity)) { var cv = sb.ToString(); var di = new VOLUME_INFO(); foreach (var V in LogicalDrives) if (V.Value.IndexOf(cv, StringComparison.OrdinalIgnoreCase) != -1) { di.Drive = V.Key; di.VolumeMountPoint = cv; di.DevicePath = didd.devicePath; foreach (var de in DiskNumbers[V.Key]) { di.DiskNumbers.Add(de.DiskNumber); di.ExtentLengths.Add(de.ExtentLength); di.StartingOffsets.Add(de.StartingOffset); } _volumepaths.Add(di.Drive.Trim(), di); break; } } } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { break; } devIndex++; } while (true); NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo); return _volumepaths; } /// <summary> /// Retrieves a list of All devices installed starting at the base root /// </summary> public Dictionary<string, DEVICE_INFO> GetAllGUIDs() { var DevList = new Dictionary<string, DEVICE_INFO>(); var classGuid = Guid.Empty; var hDevInfo = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.ALLCLASSES | NativeWin32.DICFG.PRESENT); if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE) throw new Exception("read hardware information error"); var devIndex = 0; do { var devInfoData = new NativeWin32.SP_DEVINFO_DATA(); devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA)); devInfoData.classGuid = Guid.Empty; devInfoData.devInst = 0; devInfoData.reserved = UIntPtr.Zero; if (!NativeWin32.SetupDiEnumDeviceInfo(hDevInfo, devIndex, ref devInfoData)) break; var di = new DEVICE_INFO(); di.Guid = $"{devInfoData.classGuid}"; di.ClassName = $"{GetClassNameFromGuid(devInfoData.classGuid)}"; di.Description = $"{GetClassDescriptionFromGuid(devInfoData.classGuid)}"; di.InstanceID = $"{GetDeviceInstanceId(hDevInfo, devInfoData)}"; di.DI_Capabilities = (NativeWin32.DeviceCapabilities) GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Capabilities, 0); di.FriendlyName = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.FriendlyName, null); di.ClassGuid = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.ClassGuid, null); di.HardwareId = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.HardwareId, null); di.Mfg = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Mfg, null); di.EnumeratorName = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.EnumeratorName, null); if (!DevList.ContainsValue(di)) DevList.Add($"{devIndex}", di); devIndex++; } while (true); NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo); return DevList; } /// <summary> /// Get a list of all devices starting a a specified Guid base. /// </summary> public IEnumerable<DEVICE_INFO> GetGUIDGroup(string ClassName) { if (ClassNames.All(c => c.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) == -1)) throw new Exception("ClassName is Invalid."); var dl = GetAllGUIDs(); return dl.Values.Where(d => d.ClassName.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) != -1); } /// <summary> /// property is SPDRP type /// </summary> private static uint GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, uint defaultValue) { var propertyRegDataType = 0u; var requiredSize = IntPtr.Zero; var propertyBufferSize = (uint) Marshal.SizeOf(typeof(uint)); var propertyBuffer = Marshal.AllocHGlobal((int) propertyBufferSize); if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize)) { var error = Marshal.GetLastWin32Error(); if (error != NativeWin32.ERROR_INVALID_DATA) throw new Win32Exception(error); return defaultValue; } var value = (uint) Marshal.PtrToStructure(propertyBuffer, typeof(uint)); Marshal.FreeHGlobal(propertyBuffer); return value; } /// <summary> /// property is SPDRP type /// </summary> private Guid GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devData, uint property, Guid defaultValue) { var propertyRegDataType = 0u; var requiredSize = IntPtr.Zero; var propertyBufferSize = (uint) Marshal.SizeOf(typeof(Guid)); var propertyBuffer = Marshal.AllocHGlobal((int) propertyBufferSize); if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize)) { Marshal.FreeHGlobal(propertyBuffer); var error = Marshal.GetLastWin32Error(); if (error != NativeWin32.ERROR_INVALID_DATA) throw new Win32Exception(error); return defaultValue; } var value = (Guid) Marshal.PtrToStructure(propertyBuffer, typeof(Guid)); Marshal.FreeHGlobal(propertyBuffer); return value; } /// <summary> /// property is SPDRP type /// </summary> private string GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, string defaultValue) { var propertyRegDataType = 0u; var requiredSize = IntPtr.Zero; var propertyBufferSize = 1024u; var propertyBuffer = new StringBuilder((int) propertyBufferSize); if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize)) { var error = Marshal.GetLastWin32Error(); if (error != NativeWin32.ERROR_INVALID_DATA) throw new Win32Exception(error); return defaultValue; } return propertyBuffer.ToString(); } public string GetClassNameFromGuid(Guid guid) { var result = string.Empty; var className = new StringBuilder(); var iRequiredSize = 0; var iSize = 0; var b = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize); className = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize); if (b) result = className.ToString(); return result; } public string GetClassDescriptionFromGuid(Guid guid) { var result = string.Empty; var classDesc = new StringBuilder(0); var iRequiredSize = 0; var iSize = 0; var b = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize); classDesc = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize); if (b) result = classDesc.ToString(); return result; } public string GetDeviceInstanceId(IntPtr DeviceInfoSet, NativeWin32.SP_DEVINFO_DATA DeviceInfoData) { var result = string.Empty; var id = new StringBuilder(0); var iRequiredSize = 0; var iSize = 0; var b = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize); id = new StringBuilder(iRequiredSize); iSize = iRequiredSize; b = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize); if (b) result = id.ToString(); return result; } public class VOLUME_INFO { public string DevicePath; public List<uint> DiskNumbers = new List<uint>(); public string Drive; public List<ulong> ExtentLengths = new List<ulong>(); public List<ulong> StartingOffsets = new List<ulong>(); public string VolumeMountPoint; } public struct DEVICE_INFO { public string Guid; public string ClassName; public string Description; public string InstanceID; public NativeWin32.DeviceCapabilities DI_Capabilities; public string FriendlyName; public string ClassGuid; public string HardwareId; public string Mfg; public string EnumeratorName; public string Drive; public string VolumePath; } }
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
public class SetupDi
{
    /// <summary>
    ///     Class Names (Incomplete??)
    /// </summary>
    public string[] ClassNames =
    {
        "AudioEndpoint",
        "CDROM",
        "Computer",
        "DiskDrive",
        "Display",
        "DriverInterface",
        "HDC",
        "HIDClass",
        "Keyboard",
        "MEDIA",
        "Monitor",
        "Mouse",
        "Net",
        "PrintQueue",
        "Processor",
        "SCSIAdapter",
        "Sensor",
        "SoftwareDevice",
        "System",
        "USB",
        "Volume",
        "VolumeSnapshot",
        "WPD"
    };
    /// <summary>
    ///     Are we running using 64 or 32 bits
    /// </summary>
    public bool Is64Bit => IntPtr.Size == 8;
    /// <summary>
    ///     32 and 64 bit operations require differing offsets within the pointer return values
    /// </summary>
    public uint OffsetSize => Is64Bit ? 8u : 6u;
    /// <summary>
    ///     Get a list of active drives on the PC.
    /// </summary>
    public IEnumerable<string> GetDriveReadyList => from driveInfo in DriveInfo.GetDrives() where driveInfo.IsReady select driveInfo.Name;
    /// <summary>
    ///     Get a list of drive: letters and associated MountPoints for active drives.
    /// </summary>
    public Dictionary<string, string> LogicalDrives
    {
        get
        {
            var _logicalDrives = new Dictionary<string, string>();
            foreach (var drive in GetDriveReadyList)
            {
                var sb = new StringBuilder(1024);
                if (NativeWin32.GetVolumeNameForVolumeMountPoint(drive, sb, sb.Capacity))
                    _logicalDrives.Add(drive.Replace("\\", ""), sb.ToString());
            }
            return _logicalDrives;
        }
    }
    /// <summary>
    ///     Get a list of Drive: -> Disk numbers and associated length and offset information
    /// </summary>
    public Dictionary<string, List<NativeWin32.DISK_EXTENT>> DiskNumbers
    {
        get
        {
            var _diskNumbers = new Dictionary<string, List<NativeWin32.DISK_EXTENT>>();
            foreach (var ld in LogicalDrives)
            {
                var dkexts = new List<NativeWin32.DISK_EXTENT>();
                var hFile  = NativeWin32.CreateFile(@"\\.\" + ld.Key, NativeWin32.GENERIC_READ, NativeWin32.FILE_SHARE_READ | NativeWin32.FILE_SHARE_WRITE, IntPtr.Zero, NativeWin32.OPEN_EXISTING, 0, IntPtr.Zero);
                if (hFile == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                var size          = 1024;
                var buffer        = Marshal.AllocHGlobal(size);
                var alloced       = buffer;
                var bytesReturned = 0;
                try
                {
                    if (!NativeWin32.DeviceIoControl(hFile, NativeWin32.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, IntPtr.Zero, 0, buffer, size, out bytesReturned, IntPtr.Zero))
                    {
                    }
                }
                catch (Exception e)
                {
                    var m = e.Message;
                }
                finally
                {
                    NativeWin32.CloseHandle(hFile);
                }
                if (bytesReturned > 0)
                {
                    var vde     = new NativeWin32.VOLUME_DISK_EXTENTS();
                    var dextent = new NativeWin32.DISK_EXTENT();
                    Marshal.PtrToStructure(buffer, vde);
                    buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(vde));
                    for (var i = 0; i < vde.NumberOfDiskExtents; i++)
                    {
                        dextent = (NativeWin32.DISK_EXTENT) Marshal.PtrToStructure(buffer, typeof(NativeWin32.DISK_EXTENT));
                        dkexts.Add(dextent);
                        buffer = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(dextent));
                    }
                }
                Marshal.FreeHGlobal(alloced);
                _diskNumbers.Add(ld.Key, dkexts);
            }
            return _diskNumbers;
        }
    }
    /// <summary>
    ///     Associates a drive letter to its physical disk numbers using SetupDi API functions.
    /// </summary>
    /// <returns></returns>
    public Dictionary<string, VOLUME_INFO> GetVolumePaths()
    {
        var _volumepaths = new Dictionary<string, VOLUME_INFO>();
        var classGuid    = NativeWin32.GUID_DEVINTERFACE.GUID_DEVINTERFACE_VOLUME;
        var hDevInfo     = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.DEVICEINTERFACE | NativeWin32.DICFG.PRESENT);
        if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
            throw new Exception("read hardware information error");
        var devIndex = 0u;
        do
        {
            var dia = new NativeWin32.SP_DEVICE_INTERFACE_DATA();
            dia.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVICE_INTERFACE_DATA));
            if (NativeWin32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, classGuid, devIndex, ref dia))
            {
                var didd = new NativeWin32.SP_DEVICE_INTERFACE_DETAIL_DATA();
                didd.cbSize = OffsetSize;
                var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
                devInfoData.cbSize = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
                uint nRequiredSize = 0;
                uint nBytes        = 1024;
                if (NativeWin32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref dia, ref didd, nBytes, ref nRequiredSize, ref devInfoData))
                {
                    var sb = new StringBuilder(1024);
                    if (NativeWin32.GetVolumeNameForVolumeMountPoint(didd.devicePath + @"\", sb, sb.Capacity))
                    {
                        var cv = sb.ToString();
                        var di = new VOLUME_INFO();
                        foreach (var V in LogicalDrives)
                            if (V.Value.IndexOf(cv, StringComparison.OrdinalIgnoreCase) != -1)
                            {
                                di.Drive            = V.Key;
                                di.VolumeMountPoint = cv;
                                di.DevicePath       = didd.devicePath;
                                foreach (var de in DiskNumbers[V.Key])
                                {
                                    di.DiskNumbers.Add(de.DiskNumber);
                                    di.ExtentLengths.Add(de.ExtentLength);
                                    di.StartingOffsets.Add(de.StartingOffset);
                                }
                                _volumepaths.Add(di.Drive.Trim(), di);
                                break;
                            }
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            else
            {
                break;
            }
            devIndex++;
        } while (true);
        NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
        return _volumepaths;
    }
    /// <summary>
    ///     Retrieves a list of All devices installed starting at the base root
    /// </summary>
    public Dictionary<string, DEVICE_INFO> GetAllGUIDs()
    {
        var DevList   = new Dictionary<string, DEVICE_INFO>();
        var classGuid = Guid.Empty;
        var hDevInfo  = NativeWin32.SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, NativeWin32.DICFG.ALLCLASSES | NativeWin32.DICFG.PRESENT);
        if (hDevInfo == (IntPtr) NativeWin32.INVALID_HANDLE_VALUE)
            throw new Exception("read hardware information error");
        var devIndex = 0;
        do
        {
            var devInfoData = new NativeWin32.SP_DEVINFO_DATA();
            devInfoData.cbSize    = (uint) Marshal.SizeOf(typeof(NativeWin32.SP_DEVINFO_DATA));
            devInfoData.classGuid = Guid.Empty;
            devInfoData.devInst   = 0;
            devInfoData.reserved  = UIntPtr.Zero;
            if (!NativeWin32.SetupDiEnumDeviceInfo(hDevInfo, devIndex, ref devInfoData))
                break;
            var di = new DEVICE_INFO();
            di.Guid            = $"{devInfoData.classGuid}";
            di.ClassName       = $"{GetClassNameFromGuid(devInfoData.classGuid)}";
            di.Description     = $"{GetClassDescriptionFromGuid(devInfoData.classGuid)}";
            di.InstanceID      = $"{GetDeviceInstanceId(hDevInfo, devInfoData)}";
            di.DI_Capabilities = (NativeWin32.DeviceCapabilities) GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Capabilities, 0);
            di.FriendlyName    = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.FriendlyName,   null);
            di.ClassGuid       = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.ClassGuid,      null);
            di.HardwareId      = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.HardwareId,     null);
            di.Mfg             = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.Mfg,            null);
            di.EnumeratorName  = GetProperty(hDevInfo, devInfoData, (uint) NativeWin32.SPDRP.EnumeratorName, null);
            if (!DevList.ContainsValue(di))
                DevList.Add($"{devIndex}", di);
            devIndex++;
        } while (true);
        NativeWin32.SetupDiDestroyDeviceInfoList(hDevInfo);
        return DevList;
    }
    /// <summary>
    ///     Get a list of all devices starting a a specified Guid base.
    /// </summary>
    public IEnumerable<DEVICE_INFO> GetGUIDGroup(string ClassName)
    {
        if (ClassNames.All(c => c.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) == -1))
            throw new Exception("ClassName is Invalid.");
        var dl = GetAllGUIDs();
        return dl.Values.Where(d => d.ClassName.IndexOf(ClassName, StringComparison.OrdinalIgnoreCase) != -1);
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private static uint GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, uint defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = (uint) Marshal.SizeOf(typeof(uint));
        var propertyBuffer      = Marshal.AllocHGlobal((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        var value = (uint) Marshal.PtrToStructure(propertyBuffer, typeof(uint));
        Marshal.FreeHGlobal(propertyBuffer);
        return value;
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private Guid GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devData, uint property, Guid defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = (uint) Marshal.SizeOf(typeof(Guid));
        var propertyBuffer      = Marshal.AllocHGlobal((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            Marshal.FreeHGlobal(propertyBuffer);
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        var value = (Guid) Marshal.PtrToStructure(propertyBuffer, typeof(Guid));
        Marshal.FreeHGlobal(propertyBuffer);
        return value;
    }
    /// <summary>
    ///     property is SPDRP type
    /// </summary>
    private string GetProperty(IntPtr deviceInfoSet, NativeWin32.SP_DEVINFO_DATA devInfoData, uint property, string defaultValue)
    {
        var propertyRegDataType = 0u;
        var requiredSize        = IntPtr.Zero;
        var propertyBufferSize  = 1024u;
        var propertyBuffer      = new StringBuilder((int) propertyBufferSize);
        if (!NativeWin32.SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref devInfoData, property, propertyRegDataType, propertyBuffer, propertyBufferSize, requiredSize))
        {
            var error = Marshal.GetLastWin32Error();
            if (error != NativeWin32.ERROR_INVALID_DATA)
                throw new Win32Exception(error);
            return defaultValue;
        }
        return propertyBuffer.ToString();
    }
    public string GetClassNameFromGuid(Guid guid)
    {
        var result        = string.Empty;
        var className     = new StringBuilder();
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
        className = new StringBuilder(iRequiredSize);
        iSize     = iRequiredSize;
        b         = NativeWin32.SetupDiClassNameFromGuid(ref guid, className, iSize, ref iRequiredSize);
        if (b)
            result = className.ToString();
        return result;
    }
    public string GetClassDescriptionFromGuid(Guid guid)
    {
        var result        = string.Empty;
        var classDesc     = new StringBuilder(0);
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
        classDesc = new StringBuilder(iRequiredSize);
        iSize     = iRequiredSize;
        b         = NativeWin32.SetupDiGetClassDescription(ref guid, classDesc, iSize, ref iRequiredSize);
        if (b)
            result = classDesc.ToString();
        return result;
    }
    public string GetDeviceInstanceId(IntPtr DeviceInfoSet, NativeWin32.SP_DEVINFO_DATA DeviceInfoData)
    {
        var result        = string.Empty;
        var id            = new StringBuilder(0);
        var iRequiredSize = 0;
        var iSize         = 0;
        var b             = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
        id    = new StringBuilder(iRequiredSize);
        iSize = iRequiredSize;
        b     = NativeWin32.SetupDiGetDeviceInstanceId(DeviceInfoSet, ref DeviceInfoData, id, iSize, ref iRequiredSize);
        if (b)
            result = id.ToString();
        return result;
    }
    public class VOLUME_INFO
    {
        public string      DevicePath;
        public List<uint>  DiskNumbers = new List<uint>();
        public string      Drive;
        public List<ulong> ExtentLengths   = new List<ulong>();
        public List<ulong> StartingOffsets = new List<ulong>();
        public string      VolumeMountPoint;
    }
    public struct DEVICE_INFO
    {
        public string                         Guid;
        public string                         ClassName;
        public string                         Description;
        public string                         InstanceID;
        public NativeWin32.DeviceCapabilities DI_Capabilities;
        public string                         FriendlyName;
        public string                         ClassGuid;
        public string                         HardwareId;
        public string                         Mfg;
        public string                         EnumeratorName;
        public string                         Drive;
        public string                         VolumePath;
    }
}