用uint[]来表示非负大数,其中数组开头是大数的最高32位,数组结尾是大数最低32位。
///
/// 数组转为非负大整数
///
private static BigInteger ValueOf(uint[] value)
{
var result = BigInteger.Zero;
var array = value.SkipWhile(num => num == 0u).ToArray();
foreach (var num in array)
{
result <<= 32;
result |= (num & 0xFFFF_FFFF);
}
return result;
}
///
/// 非负大整数转为 数组
///
private static uint[] ToIntArray(BigInteger value)
{
var byteCount = value.GetByteCount();
var len = (int)Math.Ceiling(byteCount / 4d);
var result = new uint[len];
for (var i = len - 1; i >= 0; --i)
{
result[i] = (uint)(value & 0xFFFF_FFFF);
value >>= 32;
}
return result;
}
private static readonly long LONG_MASK = 0xFFFF_FFFFL;
///
/// 大数加法,数组第一个int存放最高32位,最后一个int存放最低32位。
///
public static uint[] AddNonegative(uint[] left, uint[] right)
{
// If left is shorter, swap the two arrays
if (left.Length < right.Length)
{
var tmp = left;
left = right;
right = tmp;
}
var xIndex = left.Length;
var yIndex = right.Length;
var result = new uint[xIndex];
var sum = 0L;
if (yIndex == 1)
{
sum = (left[--xIndex] & LONG_MASK) + (right[0] & LONG_MASK);
result[xIndex] = (uint)sum;
}
else
{
// Add common parts of both numbers
while (yIndex > 0)
{
sum = (left[--xIndex] & LONG_MASK) +
(right[--yIndex] & LONG_MASK) + (long)((ulong)sum >> 32);
result[xIndex] = (uint)sum;
}
}
// Copy remainder of longer number while carry propagation is required
var carry = ((ulong)sum >> 32) != 0;
while (xIndex > 0 && carry)
carry = ((result[--xIndex] = left[xIndex] + 1) == 0);
// Copy remainder of longer number
while (xIndex > 0)
result[--xIndex] = left[xIndex];
// Grow result if necessary
if (carry)
{
Array.Resize(ref result, result.Length + 1);
for (var i = result.Length - 1; i > 0; --i)
result[i] = result[i - 1];
result[0] = 0x01;
}
return result;
}
///
/// 大数减法, 大于 。数组第一个int存放最高32位,最后一个int存放最低32位。
///
public static uint[] SubtractNonegative(uint[] left, uint[] right)
{
var bigIndex = left.Length;
var result = new uint[bigIndex];
var littleIndex = right.Length;
var difference = 0L;
// Subtract common parts of both numbers
while (littleIndex > 0)
{
difference = (left[--bigIndex] & LONG_MASK) -
(right[--littleIndex] & LONG_MASK) +
(difference >> 32);
result[bigIndex] = (uint)difference;
}
// Subtract remainder of longer number while borrow propagates
var borrow = (difference >> 32) != 0;
while (bigIndex > 0 && borrow)
borrow = ((result[--bigIndex] = left[bigIndex] - 1) == uint.MaxValue);
// Copy remainder of longer number
while (bigIndex > 0)
result[--bigIndex] = left[bigIndex];
return result;
}
用(uint[], bool)来表示有符号大数,其中uint[]是大数的绝对值,bool为false时是负数。
///
/// ( [], ) to
///
private BigInteger ValueOf((uint[], bool) value)
{
var result = BigInteger.Zero;
var array = value.Item1.SkipWhile(num => num == 0u).ToArray();
foreach (var num in array)
{
result <<= 32;
result |= (num & 0xFFFF_FFFF);
}
return value.Item2 ? result : -result;
}
///
/// to ( [], )
///
private (uint[], bool) ToTuple(BigInteger value)
{
var positive = BigInteger.Abs(value);
var byteCount = positive.GetByteCount();
var len = (int)Math.Ceiling(byteCount / 4d);
var result = new uint[len];
for (var i = len - 1; i >= 0; --i)
{
result[i] = (uint)(positive & 0xFFFF_FFFF);
positive >>= 32;
}
return (result, value >= 0);
}
private static readonly (uint[], bool) ZERO = (new uint[] { 0 }, true);
///
/// 大数加法,数组第一个 存放最高32位,最后一个 存放最低32位。
///
public static (uint[], bool) Add((uint[], bool) left, (uint[], bool) right)
{
left = StripLeadingZeroInts(left);
right = StripLeadingZeroInts(right);
if (IsZero(left))
return right;
if (IsZero(right))
return left;
uint[] result;
var isNonegative = left.Item2;
if (left.Item2 == right.Item2)//正负相同
{
result = AddNonegative(left.Item1, right.Item1);
}
else
{
var cmp = Compare(left.Item1, right.Item1);
if (cmp < 0)//abs(left) < abs(right)
{
result = SubtractNonegative(right.Item1, left.Item1);
isNonegative = right.Item2;
}
else
{
result = SubtractNonegative(left.Item1, right.Item1);
}
}
result = StripLeadingZeroInts(result);
return (result, isNonegative);
}
private static int Compare(uint[] left, uint[] right)
{
var len1 = left.Length;
var len2 = right.Length;
if (len1 < len2)
return -1;
if (len1 > len2)
return 1;
for (var i = 0; i < len1; i++)
{
var a = left[i];
var b = right[i];
if (a != b)
return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
}
return 0;
}
private static bool IsZero((uint[], bool) value)
=> value.Item1.Count() == 0 || value == ZERO;
private static uint[] StripLeadingZeroInts(uint[] value)
=> value.SkipWhile(num => num == 0u).ToArray();
private static (uint[], bool) StripLeadingZeroInts((uint[], bool) value)
=> (StripLeadingZeroInts(value.Item1), value.Item2);
///
/// 大数减法,数组第一个 存放最高32位,最后一个 存放最低32位。
///
public static (uint[], bool) Subtract((uint[], bool) left, (uint[], bool) right)
{
left = StripLeadingZeroInts(left);
right = StripLeadingZeroInts(right);
if (IsZero(left))
return Negate(right);
if (IsZero(right))
return left;
uint[] result;
var isNonegative = left.Item2;
if (left.Item2 != right.Item2)//正负相异
{
result = AddNonegative(left.Item1, right.Item1);
}
else
{
var cmp = Compare(left.Item1, right.Item1);
if (cmp == 0)//abs(left) == abs(right)
return ZERO;
else if (cmp < 0)//abs(left) < abs(right)
{
result = SubtractNonegative(right.Item1, left.Item1);
isNonegative = !right.Item2;
}
else
{
result = SubtractNonegative(left.Item1, right.Item1);
}
}
result = StripLeadingZeroInts(result);
return (result, isNonegative);
}
private static (uint[], bool) Negate((uint[], bool) value)
=> (value.Item1, !value.Item2);
测试
[TestMethod]
public void AddNonegativeTest()
{
for(var i = 0; i < 100; ++i)
{
var left = BigInteger.Abs(RandomBigInteger());
var right = BigInteger.Abs(RandomBigInteger());
var test = AddNonegative(ToIntArray(left), ToIntArray(right));
var expected = left + right;
Assert.AreEqual(expected, ValueOf(test));
}
}
[TestMethod]
public void AddTest()
{
for (var i = 0; i < 100; ++i)
{
var left = RandomBigInteger();
var right = RandomBigInteger();
var test = Add(ToTuple(left), ToTuple(right));
var expected = left + right;
Assert.AreEqual(expected, ValueOf(test));
}
}
[TestMethod]
public void SubtractNonegativeTest()
{
for (var i = 0; i < 100; ++i)
{
var left = BigInteger.Abs(RandomBigInteger());
var right = BigInteger.Abs(RandomBigInteger());
while (right > left) right >>= 1;
var test = SubtractNonegative(ToIntArray(left), ToIntArray(right));
var expected = left - right;
Assert.AreEqual(expected, ValueOf(test));
}
}
[TestMethod]
public void SubtractTest()
{
for (var i = 0; i < 100; ++i)
{
var left = RandomBigInteger();
var right = RandomBigInteger();
var test = Subtract(ToTuple(left), ToTuple(right));
var expected = left - right;
Assert.AreEqual(expected, ValueOf(test));
}
}
[TestMethod]
public void ConvertTest()
{
for (var i = 0; i < 100; ++i)
{
var value = BigInteger.Abs(RandomBigInteger());
var test = ToIntArray(value);
Assert.AreEqual(value, ValueOf(test));
var value2 = RandomBigInteger();
var test2 = ToTuple(value2);
Assert.AreEqual(value2, ValueOf(test2));
}
}
private BigInteger RandomBigInteger()
{
var ran = new Random(Guid.NewGuid().GetHashCode());
var bytes = new byte[ran.Next(100, 3000)];
ran.NextBytes(bytes);
return new BigInteger(bytes);
}