So, I'm trying to improve some of the operations that .net 4's BigInteger class provide since the operations appear to be quadratic. I've made a rough Karatsuba implementation but it's still slower than I'd expect.
The main problem seems to be that BigInteger provides no simple way to count the number of bits and, so, I have to use BigInteger.Log(..., 2). According to Visual Studio, about 80-90% of the time is spent calculating logarithms.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
namespace Test
{
class Program
{
static BigInteger Karatsuba(BigInteger x, BigInteger y)
{
int n = (int)Math.Max(BigInteger.Log(x, 2), BigInteger.Log(y, 2));
if (n <= 10000) return x * y;
n = ((n+1) / 2);
BigInteger b = x >> n;
BigInteger a = x - (b << n);
BigInteger d = y >> n;
BigInteger c = y - (d << n);
BigInteger ac = Karatsuba(a, c);
BigInteger bd = Karatsuba(b, d);
BigInteger abcd = Karatsuba(a+b, c+d);
return ac + ((abcd - ac - bd) << n) + (bd << (2 * n));
}
static void Main(string[] args)
{
BigInteger x = BigInteger.One << 500000 - 1;
BigInteger y = BigInteger.One << 600000 + 1;
BigInteger z = 0, q;
Console.WriteLine("Working...");
DateTime t;
// Test standard multiplication
t = DateTime.Now;
z = x * y;
Console.WriteLine(DateTime.Now - t);
// Test Karatsuba multiplication
t = DateTime.Now;
q = Karatsuba(x, y);
Console.WriteLine(DateTime.Now - t);
// Check they're equal
Console.WriteLine(z == q);
Console.Read();
}
}
}
So, what can I do to speed it up?
Why count all of the bits?
In vb I do this:
<Runtime.CompilerServices.Extension()> _
Function BitLength(ByVal n As BigInteger) As Integer
Dim Data() As Byte = n.ToByteArray
Dim result As Integer = (Data.Length - 1) * 8
Dim Msb As Byte = Data(Data.Length - 1)
While Msb
result += 1
Msb >>= 1
End While
Return result
End Function
In C# it would be:
public static int BitLength(this BigInteger n)
{
byte[] Data = n.ToByteArray();
int result = (Data.Length - 1) * 8;
byte Msb = Data[Data.Length - 1];
while (Msb != 0) {
result += 1;
Msb >>= 1;
}
return result;
}
Finally...
static BigInteger Karatsuba(BigInteger x, BigInteger y)
{
int n = (int)Math.Max(x.BitLength(), y.BitLength());
if (n <= 10000) return x * y;
n = ((n+1) / 2);
BigInteger b = x >> n;
BigInteger a = x - (b << n);
BigInteger d = y >> n;
BigInteger c = y - (d << n);
BigInteger ac = Karatsuba(a, c);
BigInteger bd = Karatsuba(b, d);
BigInteger abcd = Karatsuba(a+b, c+d);
return ac + ((abcd - ac - bd) << n) + (bd << (2 * n));
}
Calling the extension method may slow things down so perhaps this would be faster:
int n = (int)Math.Max(BitLength(x), BitLength(y));
FYI: with the bit length method you can also calculate a good approximation of the log much faster than the BigInteger Method.
bits = BitLength(a) - 1;
log_a = (double)i * log(2.0);
As far as accessing the internal UInt32 Array of the BigInteger structure, here is a hack for that.
import the reflection namespace
Private Shared ArrM As MethodInfo
Private Shard Bits As FieldInfo
Shared Sub New()
ArrM = GetType(System.Numerics.BigInteger).GetMethod("ToUInt32Array", BindingFlags.NonPublic Or BindingFlags.Instance)
Bits = GetType(System.Numerics.BigInteger).GetMember("_bits", BindingFlags.NonPublic Or BindingFlags.Instance)(0)
End Sub
<Extension()> _
Public Function ToUInt32Array(ByVal Value As System.Numerics.BigInteger) As UInteger()
Dim Result() As UInteger = ArrM.Invoke(Value, Nothing)
If Result(Result.Length - 1) = 0 Then
ReDim Preserve Result(Result.Length - 2)
End If
Return Result
End Function
Then you can get the underlying UInteger() of the big integer as
Dim Data() As UInteger = ToUInt32Array(Value)
Length = Data.Length
or Alternately
Dim Data() As UInteger = Value.ToUInt32Array()
Note that _bits fieldinfo can be used to directly access the underlying UInteger() _bits field of the BigInteger structure. This is faster than invoking the ToUInt32Array() method. However, when BigInteger B <= UInteger.MaxValue _bits is nothing. I suspect that as an optimization when a BigInteger fits the size of a 32 bit (machine size) word MS returns performs normal machine word arithmetic using the native data type.
I have also not been able to use the _bits.SetValue(B, Data()) as you normally would be able to using reflection. To work around this I use the BigInteger(bytes() b) constructor which has overhead. In c# you can use unsafe pointer operations to cast a UInteger() to Byte(). Since there are no pointer ops in VB, I use Buffer.BlockCopy. When access the data this way it is important to note that if the MSB of the bytes() array is set, MS interprets it as a Negative number. I would prefer they made a constructor with a separate sign field. The word array is to add an addition 0 byte to make uncheck the MSB
Also, when squaring you can improve even further
Function KaratsubaSquare(ByVal x As BigInteger)
Dim n As Integer = BitLength(x) 'Math.Max(BitLength(x), BitLength(y))
If (n <= KaraCutoff) Then Return x * x
n = ((n + 1) >> 1)
Dim b As BigInteger = x >> n
Dim a As BigInteger = x - (b << n)
Dim ac As BigInteger = KaratsubaSquare(a)
Dim bd As BigInteger = KaratsubaSquare(b)
Dim c As BigInteger = Karatsuba(a, b)
Return ac + (c << (n + 1)) + (bd << (2 * n))
End Function
This eliminates 2 shifts, 2 additions and 3 subtractions from each recursion of your multiplication algorithm.
Related
I'm having troubles solving modulo in c#. The example below
7^-1 modulo 26
when on Wolfram Alpha returns correct 15. In c# when I tried direct:
1/7 % 26
it returns unwanted 0.142857142857143 instead of desired 15.
But i'm not a master mathematician, so i'm probably missing something vital.
Your are looking for modular inversion: in case of
7**-1 modulo 26 = x
or
1 / 7 modulo 26 = x
you actually want to find out an x such that
(x * 7) modulo 26 = 1
In our case x == 15 since
15 * 7 == 105 == 26 * 4 + 1
For small modulo values (like 26) you can find the answer (15) with a help of naive for loop:
int modulo = 26;
int div = 7;
int result = 0;
for (int i = 1; i < modulo; ++i)
if ((i * div) % modulo == 1) {
result = i;
break;
}
Console.Write(result);
In general case, you can obtain the result with a help of Extended Euclid Algorithm. Often, when working with modulo arithmetics we face huge numbers, that's why let me show the code for BigInteger; if it's not your case you can turn BigInteger to good old int.
Code:
using System.Numerics;
...
private static (BigInteger LeftFactor,
BigInteger RightFactor,
BigInteger Gcd) Egcd(this BigInteger left, BigInteger right) {
BigInteger leftFactor = 0;
BigInteger rightFactor = 1;
BigInteger u = 1;
BigInteger v = 0;
BigInteger gcd = 0;
while (left != 0) {
BigInteger q = right / left;
BigInteger r = right % left;
BigInteger m = leftFactor - u * q;
BigInteger n = rightFactor - v * q;
right = left;
left = r;
leftFactor = u;
rightFactor = v;
u = m;
v = n;
gcd = right;
}
return (LeftFactor: leftFactor,
RightFactor: rightFactor,
Gcd: gcd);
}
The inversion itself will be
private static BigInteger ModInversion(BigInteger value, BigInteger modulo) {
var egcd = Egcd(value, modulo);
if (egcd.Gcd != 1)
throw new ArgumentException("Invalid modulo", nameof(modulo));
BigInteger result = egcd.LeftFactor;
if (result < 0)
result += modulo;
return result % modulo;
}
Demo:
using System.Numerics;
...
BigInteger result = ModInversion(7, 26);
Console.Write(result);
Outcome:
15
I use the biginteger class whose source , and I want to generate a biginteger number between two values min and max randomly so i used this method found on stackoverflow :
public BigInteger getRandom(int n)
{
var rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[n / 8];
rng.GetBytes(bytes);
return new BigInteger(bytes);
}
But I can not generate numbers between min and max because the parameters of this function represent the number of bits, can someone help me, thank you in advance!
min and max are also a biginteger.
Try this one:
// max exclusive (not included!)
public static BigInteger GetRandom(RNGCryptoServiceProvider rng, BigInteger min, BigInteger max)
{
// shift to 0...max-min
BigInteger max2 = max - min;
int bits = max2.bitCount();
// 1 bit for sign (that we will ignore, we only want positive numbers!)
bits++;
// we round to the next byte
int bytes = (bits + 7) / 8;
int uselessBits = bytes * 8 - bits;
var bytes2 = new byte[bytes];
while (true)
{
rng.GetBytes(bytes2);
// The maximum number of useless bits is 1 (sign) + 7 (rounding) == 8
if (uselessBits == 8)
{
// and it is exactly one byte!
bytes2[0] = 0;
}
else
{
// Remove the sign and the useless bits
for (int i = 0; i < uselessBits; i++)
{
//Equivalent to
//byte bit = (byte)(1 << (7 - (i % 8)));
byte bit = (byte)(1 << (7 & (~i)));
//Equivalent to
//bytes2[i / 8] &= (byte)~bit;
bytes2[i >> 3] &= (byte)~bit;
}
}
var bi = new BigInteger(bytes2);
// If it is too much big, then retry!
if (bi >= max2)
{
continue;
}
// unshift the number
bi += min;
return bi;
}
}
There are some comments that explain a little how it work.
I have 3 very large signed integers.
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
I want to calculate their truncated average. Expected average value is long.MaxValue - 1, which is 9223372036854775806.
It is impossible to calculate it as:
long avg = (x + y + z) / 3; // 3074457345618258600
Note: I read all those questions about average of 2 numbers, but I don't see how that technique can be applied to average of 3 numbers.
It would be very easy with the usage of BigInteger, but let's assume I cannot use it.
BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806
If I convert to double, then, of course, I lose precision:
double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000
If I convert to decimal, it works, but also let's assume that I cannot use it.
decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806
Question: Is there a way to calculate the truncated average of 3 very large integers only with the usage of long type? Don't consider that question as C#-specific, just it is easier for me to provide samples in C#.
This code will work, but isn't that pretty.
It first divides all three values (it floors the values, so you 'lose' the remainder), and then divides the remainder:
long n = x / 3
+ y / 3
+ z / 3
+ ( x % 3
+ y % 3
+ z % 3
) / 3
Note that the above sample does not always work properly when having one or more negative values.
As discussed with Ulugbek, since the number of comments are exploding below, here is the current BEST solution for both positive and negative values.
Thanks to answers and comments of Ulugbek Umirov, James S, KevinZ, Marc van Leeuwen, gnasher729 this is the current solution:
static long CalculateAverage(long x, long y, long z)
{
return (x % 3 + y % 3 + z % 3 + 6) / 3 - 2
+ x / 3 + y / 3 + z / 3;
}
static long CalculateAverage(params long[] arr)
{
int count = arr.Length;
return (arr.Sum(n => n % count) + count * (count - 1)) / count - (count - 1)
+ arr.Sum(n => n / count);
}
NB - Patrick has already given a great answer. Expanding on this you could do a generic version for any number of integers like so:
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
long[] arr = { x, y, z };
var avg = arr.Select(i => i / arr.Length).Sum()
+ arr.Select(i => i % arr.Length).Sum() / arr.Length;
Patrick Hofman has posted a great solution. But if needed it can still be implemented in several other ways. Using the algorithm here I have another solution. If implemented carefully it may be faster than the multiple divisions in systems with slow hardware divisors. It can be further optimized by using divide by constants technique from hacker's delight
public class int128_t {
private int H;
private long L;
public int128_t(int h, long l)
{
H = h;
L = l;
}
public int128_t add(int128_t a)
{
int128_t s;
s.L = L + a.L;
s.H = H + a.H + (s.L < a.L);
return b;
}
private int128_t rshift2() // right shift 2
{
int128_t r;
r.H = H >> 2;
r.L = (L >> 2) | ((H & 0x03) << 62);
return r;
}
public int128_t divideby3()
{
int128_t sum = {0, 0}, num = new int128_t(H, L);
while (num.H || num.L > 3)
{
int128_t n_sar2 = num.rshift2();
sum = add(n_sar2, sum);
num = add(n_sar2, new int128_t(0, num.L & 3));
}
if (num.H == 0 && num.L == 3)
{
// sum = add(sum, 1);
sum.L++;
if (sum.L == 0) sum.H++;
}
return sum;
}
};
int128_t t = new int128_t(0, x);
t = t.add(new int128_t(0, y));
t = t.add(new int128_t(0, z));
t = t.divideby3();
long average = t.L;
In C/C++ on 64-bit platforms it's much easier with __int128
int64_t average = ((__int128)x + y + z)/3;
You can calculate the mean of numbers based on the differences between the numbers rather than using the sum.
Let's say x is the max, y is the median, z is the min (as you have). We will call them max, median and min.
Conditional checker added as per #UlugbekUmirov's comment:
long tmp = median + ((min - median) / 2); //Average of min 2 values
if (median > 0) tmp = median + ((max - median) / 2); //Average of max 2 values
long mean;
if (min > 0) {
mean = min + ((tmp - min) * (2.0 / 3)); //Average of all 3 values
} else if (median > 0) {
mean = min;
while (mean != tmp) {
mean += 2;
tmp--;
}
} else if (max > 0) {
mean = max;
while (mean != tmp) {
mean--;
tmp += 2;
}
} else {
mean = max + ((tmp - max) * (2.0 / 3));
}
Patching Patrick Hofman's solution with supercat's correction, I give you the following:
static Int64 Avg3 ( Int64 x, Int64 y, Int64 z )
{
UInt64 flag = 1ul << 63;
UInt64 x_ = flag ^ (UInt64) x;
UInt64 y_ = flag ^ (UInt64) y;
UInt64 z_ = flag ^ (UInt64) z;
UInt64 quotient = x_ / 3ul + y_ / 3ul + z_ / 3ul
+ ( x_ % 3ul + y_ % 3ul + z_ % 3ul ) / 3ul;
return (Int64) (quotient ^ flag);
}
And the N element case:
static Int64 AvgN ( params Int64 [ ] args )
{
UInt64 length = (UInt64) args.Length;
UInt64 flag = 1ul << 63;
UInt64 quotient_sum = 0;
UInt64 remainder_sum = 0;
foreach ( Int64 item in args )
{
UInt64 uitem = flag ^ (UInt64) item;
quotient_sum += uitem / length;
remainder_sum += uitem % length;
}
return (Int64) ( flag ^ ( quotient_sum + remainder_sum / length ) );
}
This always gives the floor() of the mean, and eliminates every possible edge case.
Because C uses floored division rather than Euclidian division, it may easier to compute a properly-rounded average of three unsigned values than three signed ones. Simply add 0x8000000000000000UL to each number before taking the unsigned average, subtract it after taking the result, and use an unchecked cast back to Int64 to get a signed average.
To compute the unsigned average, compute the sum of the top 32 bits of the three values. Then compute the sum of the bottom 32 bits of the three values, plus the sum from above, plus one [the plus one is to yield a rounded result]. The average will be 0x55555555 times the first sum, plus one third of the second.
Performance on 32-bit processors might be enhanced by producing three "sum" values each of which is 32 bits long, so that the final result is ((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3; it might possibly be further enhanced by replacing sumL/3 with ((sumL * 0x55555556UL) >> 32), though the latter would depend upon the JIT optimizer [it might know how to replace a division by 3 with a multiply, and its code might actually be more efficient than an explicit multiply operation].
If you know you have N values, can you just divide each value by N and sum them together?
long GetAverage(long* arrayVals, int n)
{
long avg = 0;
long rem = 0;
for(int i=0; i<n; ++i)
{
avg += arrayVals[i] / n;
rem += arrayVals[i] % n;
}
return avg + (rem / n);
}
You could use the fact that you can write each of the numbers as y = ax + b, where x is a constant. Each a would be y / x (the integer part of that division). Each b would be y % x (the rest/modulo of that division). If you choose this constant in an intelligent way, for example by choosing the square root of the maximum number as a constant, you can get the average of x numbers without having problems with overflow.
The average of an arbitrary list of numbers can be found by finding:
( ( sum( all A's ) / length ) * constant ) +
( ( sum( all A's ) % length ) * constant / length) +
( ( sum( all B's ) / length )
where % denotes modulo and / denotes the 'whole' part of division.
The program would look something like:
class Program
{
static void Main()
{
List<long> list = new List<long>();
list.Add( long.MaxValue );
list.Add( long.MaxValue - 1 );
list.Add( long.MaxValue - 2 );
long sumA = 0, sumB = 0;
long res1, res2, res3;
//You should calculate the following dynamically
long constant = 1753413056;
foreach (long num in list)
{
sumA += num / constant;
sumB += num % constant;
}
res1 = (sumA / list.Count) * constant;
res2 = ((sumA % list.Count) * constant) / list.Count;
res3 = sumB / list.Count;
Console.WriteLine( res1 + res2 + res3 );
}
}
I also tried it and come up with a faster solution (although only by a factor about 3/4). It uses a single division
public static long avg(long a, long b, long c) {
final long quarterSum = (a>>2) + (b>>2) + (c>>2);
final long lowSum = (a&3) + (b&3) + (c&3);
final long twelfth = quarterSum / 3;
final long quarterRemainder = quarterSum - 3*twelfth;
final long adjustment = smallDiv3(lowSum + 4*quarterRemainder);
return 4*twelfth + adjustment;
}
where smallDiv3 is division by 3 using multipliation and working only for small arguments
private static long smallDiv3(long n) {
assert -30 <= n && n <= 30;
// Constants found rather experimentally.
return (64/3*n + 10) >> 6;
}
Here is the whole code including a test and a benchmark, the results are not that impressive.
This function computes the result in two divisions. It should generalize nicely to other divisors and word sizes.
It works by computing the double-word addition result, then working out the division.
Int64 average(Int64 a, Int64 b, Int64 c) {
// constants: 0x10000000000000000 div/mod 3
const Int64 hdiv3 = UInt64(-3) / 3 + 1;
const Int64 hmod3 = UInt64(-3) % 3;
// compute the signed double-word addition result in hi:lo
UInt64 lo = a; Int64 hi = a>=0 ? 0 : -1;
lo += b; hi += b>=0 ? lo<b : -(lo>=UInt64(b));
lo += c; hi += c>=0 ? lo<c : -(lo>=UInt64(c));
// divide, do a correction when high/low modulos add up
return hi>=0 ? lo/3 + hi*hdiv3 + (lo%3 + hi*hmod3)/3
: lo/3+1 + hi*hdiv3 + Int64(lo%3-3 + hi*hmod3)/3;
}
Math
(x + y + z) / 3 = x/3 + y/3 + z/3
(a[1] + a[2] + .. + a[k]) / k = a[1]/k + a[2]/k + .. + a[k]/k
Code
long calculateAverage (long a [])
{
double average = 0;
foreach (long x in a)
average += (Convert.ToDouble(x)/Convert.ToDouble(a.Length));
return Convert.ToInt64(Math.Round(average));
}
long calculateAverage_Safe (long a [])
{
double average = 0;
double b = 0;
foreach (long x in a)
{
b = (Convert.ToDouble(x)/Convert.ToDouble(a.Length));
if (b >= (Convert.ToDouble(long.MaxValue)-average))
throw new OverflowException ();
average += b;
}
return Convert.ToInt64(Math.Round(average));
}
Try this:
long n = Array.ConvertAll(new[]{x,y,z},v=>v/3).Sum()
+ (Array.ConvertAll(new[]{x,y,z},v=>v%3).Sum() / 3);
I want to convert an int to a byte[2] array using BCD.
The int in question will come from DateTime representing the Year and must be converted to two bytes.
Is there any pre-made function that does this or can you give me a simple way of doing this?
example:
int year = 2010
would output:
byte[2]{0x20, 0x10};
static byte[] Year2Bcd(int year) {
if (year < 0 || year > 9999) throw new ArgumentException();
int bcd = 0;
for (int digit = 0; digit < 4; ++digit) {
int nibble = year % 10;
bcd |= nibble << (digit * 4);
year /= 10;
}
return new byte[] { (byte)((bcd >> 8) & 0xff), (byte)(bcd & 0xff) };
}
Beware that you asked for a big-endian result, that's a bit unusual.
Use this method.
public static byte[] ToBcd(int value){
if(value<0 || value>99999999)
throw new ArgumentOutOfRangeException("value");
byte[] ret=new byte[4];
for(int i=0;i<4;i++){
ret[i]=(byte)(value%10);
value/=10;
ret[i]|=(byte)((value%10)<<4);
value/=10;
}
return ret;
}
This is essentially how it works.
If the value is less than 0 or greater than 99999999, the value won't fit in four bytes. More formally, if the value is less than 0 or is 10^(n*2) or greater, where n is the number of bytes, the value won't fit in n bytes.
For each byte:
Set that byte to the remainder of the value-divided-by-10 to the byte. (This will place the last digit in the low nibble [half-byte] of the current byte.)
Divide the value by 10.
Add 16 times the remainder of the value-divided-by-10 to the byte. (This will place the now-last digit in the high nibble of the current byte.)
Divide the value by 10.
(One optimization is to set every byte to 0 beforehand -- which is implicitly done by .NET when it allocates a new array -- and to stop iterating when the value reaches 0. This latter optimization is not done in the code above, for simplicity. Also, if available, some compilers or assemblers offer a divide/remainder routine that allows retrieving the quotient and remainder in one division step, an optimization which is not usually necessary though.)
Here's a terrible brute-force version. I'm sure there's a better way than this, but it ought to work anyway.
int digitOne = year / 1000;
int digitTwo = (year - digitOne * 1000) / 100;
int digitThree = (year - digitOne * 1000 - digitTwo * 100) / 10;
int digitFour = year - digitOne * 1000 - digitTwo * 100 - digitThree * 10;
byte[] bcdYear = new byte[] { digitOne << 4 | digitTwo, digitThree << 4 | digitFour };
The sad part about it is that fast binary to BCD conversions are built into the x86 microprocessor architecture, if you could get at them!
Here is a slightly cleaner version then Jeffrey's
static byte[] IntToBCD(int input)
{
if (input > 9999 || input < 0)
throw new ArgumentOutOfRangeException("input");
int thousands = input / 1000;
int hundreds = (input -= thousands * 1000) / 100;
int tens = (input -= hundreds * 100) / 10;
int ones = (input -= tens * 10);
byte[] bcd = new byte[] {
(byte)(thousands << 4 | hundreds),
(byte)(tens << 4 | ones)
};
return bcd;
}
maybe a simple parse function containing this loop
i=0;
while (id>0)
{
twodigits=id%100; //need 2 digits per byte
arr[i]=twodigits%10 + twodigits/10*16; //first digit on first 4 bits second digit shifted with 4 bits
id/=100;
i++;
}
More common solution
private IEnumerable<Byte> GetBytes(Decimal value)
{
Byte currentByte = 0;
Boolean odd = true;
while (value > 0)
{
if (odd)
currentByte = 0;
Decimal rest = value % 10;
value = (value-rest)/10;
currentByte |= (Byte)(odd ? (Byte)rest : (Byte)((Byte)rest << 4));
if(!odd)
yield return currentByte;
odd = !odd;
}
if(!odd)
yield return currentByte;
}
Same version as Peter O. but in VB.NET
Public Shared Function ToBcd(ByVal pValue As Integer) As Byte()
If pValue < 0 OrElse pValue > 99999999 Then Throw New ArgumentOutOfRangeException("value")
Dim ret As Byte() = New Byte(3) {} 'All bytes are init with 0's
For i As Integer = 0 To 3
ret(i) = CByte(pValue Mod 10)
pValue = Math.Floor(pValue / 10.0)
ret(i) = ret(i) Or CByte((pValue Mod 10) << 4)
pValue = Math.Floor(pValue / 10.0)
If pValue = 0 Then Exit For
Next
Return ret
End Function
The trick here is to be aware that simply using pValue /= 10 will round the value so if for instance the argument is "16", the first part of the byte will be correct, but the result of the division will be 2 (as 1.6 will be rounded up). Therefore I use the Math.Floor method.
I made a generic routine posted at IntToByteArray that you could use like:
var yearInBytes = ConvertBigIntToBcd(2010, 2);
static byte[] IntToBCD(int input) {
byte[] bcd = new byte[] {
(byte)(input>> 8),
(byte)(input& 0x00FF)
};
return bcd;
}
I have 3 byte arrays of length 128, 128, 3 bytes respectively. I don't know what it is, but I expect them to be Modulus, D, Exponent.
Now how can I use these arrays in C# to decrypt a byte array using RSA?
When I create an RSAParameters and assign the 3 byte arrays to Modulus, D, Exponent and try to use that RSAParameters in RSACryptoServiceProvider.ImportParameters, decryption fails stating corrupt keys. I guess the other entries also need to be filled DQ,DP,...etc...
How do I do that in C#? I don't have that values, is there an easy way to decrypt a byte array using only Modulus, D, Exponent in C#, as in other languages?
The Windows implementations seem to only be willing to do RSA via the CRT parameters, leaving D as a potentially ignored value. At the very least, the CRT parameters are required inputs.
First, we need to turn your arrays into BigInteger values. I'm assuming here that you have Big-Endian encoded values. If they're Little-Endian, don't call Array.Reverse() and change the copy-to index from 1 to 0.
private static BigInteger GetBigInteger(byte[] bytes)
{
byte[] signPadded = new byte[bytes.Length + 1];
Buffer.BlockCopy(bytes, 0, signPadded, 1, bytes.Length);
Array.Reverse(signPadded);
return new BigInteger(signPadded);
}
Adding the extra byte prevents numbers from being treated as negative. (One could avoid the allocation and memory copy by testing for the sign bit in the last byte, if one wanted).
So now you have three BigInteger values, n, e, d. Not sure which of n and d is which?
// Unless someone tried really hard to make this break it'll work.
if (n < d)
{
BigInteger tmp = n;
n = d;
d = tmp;
}
Now, using the algorithm from NIST Special Publication 800-56B Recommendation for Pair-Wise August 2009 Key Establishment Schemes Using Integer Factorization Cryptography, Appendix C (as shared in https://stackoverflow.com/a/28299742/6535399) we can calculate the BigInteger values. There's a tricky subtlety, though. RSAParameters values have to have a correct amount of padding, and RSACryptoServiceProvider doesn't do it for you.
private static RSAParameters RecoverRSAParameters(BigInteger n, BigInteger e, BigInteger d)
{
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
BigInteger k = d * e - 1;
if (!k.IsEven)
{
throw new InvalidOperationException("d*e - 1 is odd");
}
BigInteger two = 2;
BigInteger t = BigInteger.One;
BigInteger r = k / two;
while (r.IsEven)
{
t++;
r /= two;
}
byte[] rndBuf = n.ToByteArray();
if (rndBuf[rndBuf.Length - 1] == 0)
{
rndBuf = new byte[rndBuf.Length - 1];
}
BigInteger nMinusOne = n - BigInteger.One;
bool cracked = false;
BigInteger y = BigInteger.Zero;
for (int i = 0; i < 100 && !cracked; i++)
{
BigInteger g;
do
{
rng.GetBytes(rndBuf);
g = GetBigInteger(rndBuf);
}
while (g >= n);
y = BigInteger.ModPow(g, r, n);
if (y.IsOne || y == nMinusOne)
{
i--;
continue;
}
for (BigInteger j = BigInteger.One; j < t; j++)
{
BigInteger x = BigInteger.ModPow(y, two, n);
if (x.IsOne)
{
cracked = true;
break;
}
if (x == nMinusOne)
{
break;
}
y = x;
}
}
if (!cracked)
{
throw new InvalidOperationException("Prime factors not found");
}
BigInteger p = BigInteger.GreatestCommonDivisor(y - BigInteger.One, n);
BigInteger q = n / p;
BigInteger dp = d % (p - BigInteger.One);
BigInteger dq = d % (q - BigInteger.One);
BigInteger inverseQ = ModInverse(q, p);
int modLen = rndBuf.Length;
int halfModLen = (modLen + 1) / 2;
return new RSAParameters
{
Modulus = GetBytes(n, modLen),
Exponent = GetBytes(e, -1),
D = GetBytes(d, modLen),
P = GetBytes(p, halfModLen),
Q = GetBytes(q, halfModLen),
DP = GetBytes(dp, halfModLen),
DQ = GetBytes(dq, halfModLen),
InverseQ = GetBytes(inverseQ, halfModLen),
};
}
}
With the "tricky" BigInteger-to-suitable-for-RSAParameters-byte[] method:
private static byte[] GetBytes(BigInteger value, int size)
{
byte[] bytes = value.ToByteArray();
if (size == -1)
{
size = bytes.Length;
}
if (bytes.Length > size + 1)
{
throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
}
if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0)
{
throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
}
Array.Resize(ref bytes, size);
Array.Reverse(bytes);
return bytes;
}
And for computing InverseQ you need ModInverse:
private static BigInteger ModInverse(BigInteger e, BigInteger n)
{
BigInteger r = n;
BigInteger newR = e;
BigInteger t = 0;
BigInteger newT = 1;
while (newR != 0)
{
BigInteger quotient = r / newR;
BigInteger temp;
temp = t;
t = newT;
newT = temp - quotient * newT;
temp = r;
r = newR;
newR = temp - quotient * newR;
}
if (t < 0)
{
t = t + n;
}
return t;
}
On my computer I'm recovering P and Q from (n, e, d) in ~50ms for a 1024-bit key. ~2-4 seconds for a 4096-bit key.
Note to implementers who like unit tests: There's not really a defined order for P and Q (like a convention that P always be the larger), so your P and Q values may be backwards from an RSAParameters structure that you started with. DP and DQ will thus also be reversed.
You don't have enough when you just have Mod, D, and the exponent. (Well you might have enough) P and Q are VERY hard to calculate from the mod. I wouldn't know how to do that and there are almost certainly more primes than the right ones that multiplied end up with the same mod.
You need atleast P, Q and the public exponent.
P, Q and D are the building blocks
DP = D mod (p - 1)
DQ = D mod (q - 1)
InverseQ = Q^-1 mod p
Modulus = P * Q
so now we have
P Q and D.
and we can calulate DP, DQ, InverseQ and Modulus and Exponent (see below)
long gcd(long a, long b)
{
long temp;
while (b != 0)
{
temp = b;
b = a % b;
a = temp;
}
return a;
}
Exponent = gcd(1, (P - 1)*(Q - 1));