1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

[release/9.0] Fix BigInteger.Rotate{Left,Right} for backport (#112991)

* Add BigInteger.Rotate* tests

* Fix BigInteger.Rotate*

* avoid stackalloc

* Add comment

* Fix the unsigned right shift operator of BigInteger (#112879)

* Add tests for the shift operator of BigInteger

* Fix the unsigned right shift operator of BigInteger

* avoid stackalloc

* external sign element

---------

Co-authored-by: kzrnm <gengesa@gmail.com>
This commit is contained in:
github-actions[bot] 2025-03-10 12:10:14 -07:00 committed by GitHub
parent b64f47acec
commit 8670c12404
Signed by: github
GPG key ID: B5690EEEBB952194
7 changed files with 1123 additions and 121 deletions

View file

@ -1694,7 +1694,7 @@ namespace System.Numerics
}
if (bitsFromPool != null)
ArrayPool<uint>.Shared.Return(bitsFromPool);
ArrayPool<uint>.Shared.Return(bitsFromPool);
return result;
}
@ -2629,7 +2629,7 @@ namespace System.Numerics
if (zdFromPool != null)
ArrayPool<uint>.Shared.Return(zdFromPool);
exit:
exit:
if (xdFromPool != null)
ArrayPool<uint>.Shared.Return(xdFromPool);
@ -3232,7 +3232,27 @@ namespace System.Numerics
public static BigInteger RotateLeft(BigInteger value, int rotateAmount)
{
value.AssertValid();
int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
bool negx = value._sign < 0;
uint smallBits = NumericsHelpers.Abs(value._sign);
scoped ReadOnlySpan<uint> bits = value._bits;
if (bits.IsEmpty)
{
bits = new ReadOnlySpan<uint>(in smallBits);
}
int xl = bits.Length;
if (negx && (bits[^1] >= kuMaskHighBit) && ((bits[^1] != kuMaskHighBit) || bits.IndexOfAnyExcept(0u) != (bits.Length - 1)))
{
// We check for a special case where its sign bit could be outside the uint array after 2's complement conversion.
// For example given [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], its 2's complement is [0x01, 0x00, 0x00]
// After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back.
// The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back
// If the 2's component's last element is a 0, we will track the sign externally
++xl;
}
int byteCount = xl * 4;
// Normalize the rotate amount to drop full rotations
rotateAmount = (int)(rotateAmount % (byteCount * 8L));
@ -3249,14 +3269,13 @@ namespace System.Numerics
(int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
uint[]? xdFromPool = null;
int xl = value._bits?.Length ?? 1;
Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
xd = xd.Slice(0, xl);
xd[^1] = 0;
bool negx = value.GetPartsForBitManipulation(xd);
bits.CopyTo(xd);
int zl = xl;
uint[]? zdFromPool = null;
@ -3367,7 +3386,28 @@ namespace System.Numerics
public static BigInteger RotateRight(BigInteger value, int rotateAmount)
{
value.AssertValid();
int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
bool negx = value._sign < 0;
uint smallBits = NumericsHelpers.Abs(value._sign);
scoped ReadOnlySpan<uint> bits = value._bits;
if (bits.IsEmpty)
{
bits = new ReadOnlySpan<uint>(in smallBits);
}
int xl = bits.Length;
if (negx && (bits[^1] >= kuMaskHighBit) && ((bits[^1] != kuMaskHighBit) || bits.IndexOfAnyExcept(0u) != (bits.Length - 1)))
{
// We check for a special case where its sign bit could be outside the uint array after 2's complement conversion.
// For example given [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], its 2's complement is [0x01, 0x00, 0x00]
// After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back.
// The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back
// If the 2's component's last element is a 0, we will track the sign externally
++xl;
}
int byteCount = xl * 4;
// Normalize the rotate amount to drop full rotations
rotateAmount = (int)(rotateAmount % (byteCount * 8L));
@ -3384,14 +3424,13 @@ namespace System.Numerics
(int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
uint[]? xdFromPool = null;
int xl = value._bits?.Length ?? 1;
Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
xd = xd.Slice(0, xl);
xd[^1] = 0;
bool negx = value.GetPartsForBitManipulation(xd);
bits.CopyTo(xd);
int zl = xl;
uint[]? zdFromPool = null;
@ -3438,19 +3477,12 @@ namespace System.Numerics
{
int carryShift = kcbitUint - smallShift;
int dstIndex = 0;
int srcIndex = digitShift;
int dstIndex = xd.Length - 1;
int srcIndex = digitShift == 0
? xd.Length - 1
: digitShift - 1;
uint carry = 0;
if (digitShift == 0)
{
carry = xd[^1] << carryShift;
}
else
{
carry = xd[srcIndex - 1] << carryShift;
}
uint carry = xd[digitShift] << carryShift;
do
{
@ -3459,22 +3491,22 @@ namespace System.Numerics
zd[dstIndex] = (part >> smallShift) | carry;
carry = part << carryShift;
dstIndex++;
srcIndex++;
dstIndex--;
srcIndex--;
}
while (srcIndex < xd.Length);
while ((uint)srcIndex < (uint)xd.Length); // is equivalent to (srcIndex >= 0 && srcIndex < xd.Length)
srcIndex = 0;
srcIndex = xd.Length - 1;
while (dstIndex < zd.Length)
while ((uint)dstIndex < (uint)zd.Length) // is equivalent to (dstIndex >= 0 && dstIndex < zd.Length)
{
uint part = xd[srcIndex];
zd[dstIndex] = (part >> smallShift) | carry;
carry = part << carryShift;
dstIndex++;
srcIndex++;
dstIndex--;
srcIndex--;
}
}
@ -5232,13 +5264,32 @@ namespace System.Numerics
BigInteger result;
bool negx = value._sign < 0;
uint smallBits = NumericsHelpers.Abs(value._sign);
scoped ReadOnlySpan<uint> bits = value._bits;
if (bits.IsEmpty)
{
bits = new ReadOnlySpan<uint>(in smallBits);
}
int xl = bits.Length;
if (negx && (bits[^1] >= kuMaskHighBit) && ((bits[^1] != kuMaskHighBit) || bits.IndexOfAnyExcept(0u) != (bits.Length - 1)))
{
// For a shift of N x 32 bit,
// We check for a special case where its sign bit could be outside the uint array after 2's complement conversion.
// For example given [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], its 2's complement is [0x01, 0x00, 0x00]
// After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back.
// The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back
// If the 2's component's last element is a 0, we will track the sign externally
++xl;
}
uint[]? xdFromPool = null;
int xl = value._bits?.Length ?? 1;
Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl)).Slice(0, xl);
bool negx = value.GetPartsForBitManipulation(xd);
xd[^1] = 0;
bits.CopyTo(xd);
if (negx)
{

View file

@ -108,8 +108,14 @@ namespace System.Numerics.Tests
return new BigInteger(Max(bytes1, bytes2).ToArray());
case "b>>":
return new BigInteger(ShiftLeft(bytes1, Negate(bytes2)).ToArray());
case "b>>>":
return new BigInteger(ShiftRightUnsigned(bytes1, bytes2).ToArray());
case "b<<":
return new BigInteger(ShiftLeft(bytes1, bytes2).ToArray());
case "bRotateLeft":
return new BigInteger(RotateLeft(bytes1, bytes2).ToArray());
case "bRotateRight":
return new BigInteger(RotateLeft(bytes1, Negate(bytes2)).ToArray());
case "b^":
return new BigInteger(Xor(bytes1, bytes2).ToArray());
case "b|":
@ -637,11 +643,68 @@ namespace System.Numerics.Tests
return bnew;
}
public static List<byte> ShiftRightUnsigned(List<byte> bytes1, List<byte> bytes2)
{
int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
sbyte bitShift = (sbyte)new BigInteger(Remainder(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
if (byteShift == 0 && bitShift == 0)
return bytes1;
if (byteShift < 0 || bitShift < 0)
return ShiftLeft(bytes1, Negate(bytes2));
Trim(bytes1);
byte fill = (bytes1[bytes1.Count - 1] & 0x80) != 0 ? byte.MaxValue : (byte)0;
if (fill == byte.MaxValue)
{
while (bytes1.Count % 4 != 0)
{
bytes1.Add(fill);
}
}
if (byteShift >= bytes1.Count)
{
return [fill];
}
if (fill == byte.MaxValue)
{
bytes1.Add(0);
}
for (int i = 0; i < bitShift; i++)
{
bytes1 = ShiftRight(bytes1);
}
List<byte> temp = new List<byte>();
for (int i = byteShift; i < bytes1.Count; i++)
{
temp.Add(bytes1[i]);
}
bytes1 = temp;
if (fill == byte.MaxValue && bytes1.Count % 4 == 1)
{
bytes1.RemoveAt(bytes1.Count - 1);
}
Trim(bytes1);
return bytes1;
}
public static List<byte> ShiftLeft(List<byte> bytes1, List<byte> bytes2)
{
int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
sbyte bitShift = (sbyte)new BigInteger(Remainder(bytes2, new List<byte>(new byte[] { 8 })).ToArray());
Trim(bytes1);
for (int i = 0; i < Math.Abs(bitShift); i++)
{
if (bitShift < 0)
@ -774,6 +837,105 @@ namespace System.Numerics.Tests
return bresult;
}
public static List<byte> RotateRight(List<byte> bytes)
{
List<byte> bresult = new List<byte>();
byte bottom = (byte)(bytes[0] & 0x01);
for (int i = 0; i < bytes.Count; i++)
{
byte newbyte = bytes[i];
newbyte = (byte)(newbyte / 2);
if ((i != (bytes.Count - 1)) && ((bytes[i + 1] & 0x01) == 1))
{
newbyte += 128;
}
if ((i == (bytes.Count - 1)) && (bottom != 0))
{
newbyte += 128;
}
bresult.Add(newbyte);
}
return bresult;
}
public static List<byte> RotateLeft(List<byte> bytes)
{
List<byte> bresult = new List<byte>();
bool prevHead = (bytes[bytes.Count - 1] & 0x80) != 0;
for (int i = 0; i < bytes.Count; i++)
{
byte newbyte = bytes[i];
newbyte = (byte)(newbyte * 2);
if (prevHead)
{
newbyte += 1;
}
bresult.Add(newbyte);
prevHead = (bytes[i] & 0x80) != 0;
}
return bresult;
}
public static List<byte> RotateLeft(List<byte> bytes1, List<byte> bytes2)
{
List<byte> bytes1Copy = Copy(bytes1);
int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
sbyte bitShift = (sbyte)new BigInteger(Remainder(bytes2, new List<byte>(new byte[] { 8 })).ToArray());
Trim(bytes1);
byte fill = (bytes1[bytes1.Count - 1] & 0x80) != 0 ? byte.MaxValue : (byte)0;
if (fill == 0 && bytes1.Count > 1 && bytes1[bytes1.Count - 1] == 0)
bytes1.RemoveAt(bytes1.Count - 1);
while (bytes1.Count % 4 != 0)
{
bytes1.Add(fill);
}
byteShift %= bytes1.Count;
if (byteShift == 0 && bitShift == 0)
return bytes1Copy;
for (int i = 0; i < Math.Abs(bitShift); i++)
{
if (bitShift < 0)
{
bytes1 = RotateRight(bytes1);
}
else
{
bytes1 = RotateLeft(bytes1);
}
}
List<byte> temp = new List<byte>();
for (int i = 0; i < bytes1.Count; i++)
{
temp.Add(bytes1[(i - byteShift + bytes1.Count) % bytes1.Count]);
}
bytes1 = temp;
if (fill == 0)
bytes1.Add(0);
Trim(bytes1);
return bytes1;
}
public static List<byte> SetLength(List<byte> bytes, int size)
{
List<byte> bresult = new List<byte>();

View file

@ -0,0 +1,629 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Globalization;
using Xunit;
namespace System.Numerics.Tests
{
public abstract class RotateTestBase
{
public abstract string opstring { get; }
private static int s_samples = 10;
private static Random s_random = new Random(100);
[Fact]
public void RunRotateTests()
{
byte[] tempByteArray1;
byte[] tempByteArray2;
// Rotate Method - Large BigIntegers - large + Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = GetRandomPosByteArray(s_random, 2);
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - small + Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - 32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - All One Uint Large BigIntegers - 32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomLengthAllOnesUIntByteArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Uint 0xffffffff 0x8000000 ... Large BigIntegers - 32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - large - Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = GetRandomNegByteArray(s_random, 2);
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - small - Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - -32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)0xe0 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Large BigIntegers - 0 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)0 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - large + Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = GetRandomPosByteArray(s_random, 2);
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - small + Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - 32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - large - Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = GetRandomNegByteArray(s_random, 2);
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - small - Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - -32 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)0xe0 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Small BigIntegers - 0 bit Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)0 };
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Positive BigIntegers - Shift to 0
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomPosByteArray(s_random, 100);
tempByteArray2 = BitConverter.GetBytes(s_random.Next(8 * tempByteArray1.Length, 1000));
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(tempByteArray2);
}
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// Rotate Method - Negative BigIntegers - Shift to -1
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomNegByteArray(s_random, 100);
tempByteArray2 = BitConverter.GetBytes(s_random.Next(8 * tempByteArray1.Length, 1000));
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(tempByteArray2);
}
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
}
[Fact]
public void RunSmallTests()
{
foreach (int i in new int[] {
0,
1,
16,
31,
32,
33,
63,
64,
65,
100,
127,
128,
})
{
foreach (int shift in new int[] {
0,
-1, 1,
-16, 16,
-31, 31,
-32, 32,
-33, 33,
-63, 63,
-64, 64,
-65, 65,
-100, 100,
-127, 127,
-128, 128,
})
{
var num = Int128.One << i;
for (int k = -1; k <= 1; k++)
{
foreach (int sign in new int[] { -1, +1 })
{
Int128 value128 = sign * (num + k);
byte[] tempByteArray1 = GetRandomSmallByteArray(value128);
byte[] tempByteArray2 = GetRandomSmallByteArray(shift);
VerifyRotateString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
}
}
}
}
private static void VerifyRotateString(string opstring)
{
StackCalc sc = new StackCalc(opstring);
while (sc.DoNextOperation())
{
Assert.Equal(sc.snCalc.Peek().ToString(), sc.myCalc.Peek().ToString());
}
}
private static byte[] GetRandomSmallByteArray(Int128 num)
{
byte[] value = new byte[16];
for (int i = 0; i < value.Length; i++)
{
value[i] = (byte)num;
num >>= 8;
}
return value;
}
private static byte[] GetRandomByteArray(Random random)
{
return GetRandomByteArray(random, random.Next(0, 1024));
}
private static byte[] GetRandomByteArray(Random random, int size)
{
return MyBigIntImp.GetRandomByteArray(random, size);
}
private static byte[] GetRandomPosByteArray(Random random, int size)
{
byte[] value = new byte[size];
for (int i = 0; i < value.Length; ++i)
{
value[i] = (byte)random.Next(0, 256);
}
value[value.Length - 1] &= 0x7F;
return value;
}
private static byte[] GetRandomNegByteArray(Random random, int size)
{
byte[] value = new byte[size];
for (int i = 0; i < value.Length; ++i)
{
value[i] = (byte)random.Next(0, 256);
}
value[value.Length - 1] |= 0x80;
return value;
}
private static byte[] GetRandomLengthAllOnesUIntByteArray(Random random)
{
int gap = random.Next(0, 128);
int byteLength = 4 + gap * 4 + 1;
byte[] array = new byte[byteLength];
array[0] = 1;
array[^1] = 0xFF;
return array;
}
private static byte[] GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(Random random)
{
int gap = random.Next(0, 128);
int byteLength = 4 + gap * 4 + 1;
byte[] array = new byte[byteLength];
array[^5] = 0x80;
array[^1] = 0xFF;
return array;
}
private static string Print(byte[] bytes)
{
return MyBigIntImp.Print(bytes);
}
}
public class RotateLeftTest : RotateTestBase
{
public override string opstring => "bRotateLeft";
public static TheoryData<BigInteger, int, BigInteger> NegativeNumber_TestData = new TheoryData<BigInteger, int, BigInteger>
{
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0000)),
1,
new BigInteger(unchecked((long)0xFFFF_FFFE_0000_0001))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0000)),
2,
new BigInteger(unchecked((long)0xFFFF_FFFC_0000_0003))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0001)),
1,
new BigInteger(unchecked((long)0xFFFF_FFFE_0000_0003))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0001)),
2,
new BigInteger(unchecked((long)0xFFFF_FFFC_0000_0007))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0002)),
1,
new BigInteger(unchecked((long)0xFFFF_FFFE_0000_0005))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0002)),
2,
new BigInteger(unchecked((long)0xFFFF_FFFC_0000_000B))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0000)),
1,
new BigInteger(0x1)
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0000)),
2,
new BigInteger(0x2)
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0001)),
1,
new BigInteger(0x3)
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0001)),
2,
new BigInteger(0x6)
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0002)),
1,
new BigInteger(0x5)
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0002)),
2,
new BigInteger(0xA)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
1,
new BigInteger(0x1)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
2,
new BigInteger(0x2)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
1,
new BigInteger(0x3)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
2,
new BigInteger(0x6)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
1,
new BigInteger(0x5)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
2,
new BigInteger(0xA)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("________E_0000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("________C_0000_0000_0000_0000_0000_0003".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("________E_0000_0000_0000_0000_0000_0003".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("________C_0000_0000_0000_0000_0000_0007".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("________E_0000_0000_0000_0000_0000_0005".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("________C_0000_0000_0000_0000_0000_000B".Replace("_", ""), NumberStyles.HexNumber)
},
};
[Theory]
[MemberData(nameof(NegativeNumber_TestData))]
public void NegativeNumber(BigInteger input, int rotateAmount, BigInteger expected)
{
Assert.Equal(expected, BigInteger.RotateLeft(input, rotateAmount));
}
[Fact]
public void PowerOfTwo()
{
for (int i = 0; i < 32; i++)
{
foreach (int k in new int[] { 1, 2, 3, 10 })
{
BigInteger plus = BigInteger.One << (32 * k + i);
BigInteger minus = BigInteger.MinusOne << (32 * k + i);
Assert.Equal(BigInteger.One << (i == 31 ? 0 : (32 * k + i + 1)), BigInteger.RotateLeft(plus, 1));
Assert.Equal(BigInteger.One << i, BigInteger.RotateLeft(plus, 32));
Assert.Equal(BigInteger.One << (32 * (k - 1) + i), BigInteger.RotateLeft(plus, 32 * k));
Assert.Equal(i == 31 ? BigInteger.One : (new BigInteger(-1 << (i + 1)) << 32 * k) + 1,
BigInteger.RotateLeft(minus, 1));
Assert.Equal(new BigInteger(uint.MaxValue << i), BigInteger.RotateLeft(minus, 32));
Assert.Equal(new BigInteger(uint.MaxValue << i) << (32 * (k - 1)), BigInteger.RotateLeft(minus, 32 * k));
}
}
}
}
public class RotateRightTest : RotateTestBase
{
public override string opstring => "bRotateRight";
public static TheoryData<BigInteger, int, BigInteger> NegativeNumber_TestData = new TheoryData<BigInteger, int, BigInteger>
{
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0000)),
1,
new BigInteger(unchecked((long)0x7FFF_FFFF_8000_0000))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0000)),
2,
new BigInteger(unchecked((long)0x3FFF_FFFF_C000_0000))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0001)),
1,
new BigInteger(unchecked((int)0x8000_0000))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0001)),
2,
new BigInteger(unchecked((long)0x7FFF_FFFF_C000_0000))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0002)),
1,
new BigInteger(unchecked((long)0x7FFF_FFFF_8000_0001))
},
{
new BigInteger(unchecked((long)0xFFFF_FFFF_0000_0002)),
2,
new BigInteger(unchecked((long)0xBFFF_FFFF_C000_0000))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0000)),
1,
new BigInteger(unchecked((long)0x4000_0000_0000_0000))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0000)),
2,
new BigInteger(unchecked((long)0x2000_0000_0000_0000))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0001)),
1,
new BigInteger(unchecked((long)0xC000_0000_0000_0000))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0001)),
2,
new BigInteger(unchecked((long)0x6000_0000_0000_0000))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0002)),
1,
new BigInteger(unchecked((long)0x4000_0000_0000_0001))
},
{
new BigInteger(unchecked((long)0x8000_0000_0000_0002)),
2,
new BigInteger(unchecked((long)0xA000_0000_0000_0000))
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("4000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("2000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("C000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("6000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("4000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("8000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("A000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("7FFF_FFFF_8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("3FFF_FFFF_C000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("__________8000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("7FFF_FFFF_C000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
1,
BigInteger.Parse("7FFF_FFFF_8000_0000_0000_0000_0000_0001".Replace("_", ""), NumberStyles.HexNumber)
},
{
BigInteger.Parse("________F_0000_0000_0000_0000_0000_0002".Replace("_", ""), NumberStyles.HexNumber),
2,
BigInteger.Parse("BFFF_FFFF_C000_0000_0000_0000_0000_0000".Replace("_", ""), NumberStyles.HexNumber)
},
};
[Theory]
[MemberData(nameof(NegativeNumber_TestData))]
public void NegativeNumber(BigInteger input, int rotateAmount, BigInteger expected)
{
Assert.Equal(expected, BigInteger.RotateRight(input, rotateAmount));
}
[Fact]
public void PowerOfTwo()
{
for (int i = 0; i < 32; i++)
{
foreach (int k in new int[] { 1, 2, 3, 10 })
{
BigInteger plus = BigInteger.One << (32 * k + i);
BigInteger minus = BigInteger.MinusOne << (32 * k + i);
Assert.Equal(BigInteger.One << (32 * k + i - 1), BigInteger.RotateRight(plus, 1));
Assert.Equal(BigInteger.One << (32 * (k - 1) + i), BigInteger.RotateRight(plus, 32));
Assert.Equal(BigInteger.One << i, BigInteger.RotateRight(plus, 32 * k));
Assert.Equal(new BigInteger(uint.MaxValue << i) << (32 * k - 1), BigInteger.RotateRight(minus, 1));
Assert.Equal(new BigInteger(uint.MaxValue << i) << (32 * (k - 1)), BigInteger.RotateRight(minus, 32));
Assert.Equal(new BigInteger(uint.MaxValue << i), BigInteger.RotateRight(minus, 32 * k));
}
}
}
}
}

View file

@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Xunit;
namespace System.Numerics.Tests
@ -151,6 +152,56 @@ namespace System.Numerics.Tests
}
}
[Fact]
public void RunSmallTests()
{
foreach (int i in new int[] {
0,
1,
16,
31,
32,
33,
63,
64,
65,
100,
127,
128,
})
{
foreach (int shift in new int[] {
0,
-1, 1,
-16, 16,
-31, 31,
-32, 32,
-33, 33,
-63, 63,
-64, 64,
-65, 65,
-100, 100,
-127, 127,
-128, 128,
})
{
var num = Int128.One << i;
for (int k = -1; k <= 1; k++)
{
foreach (int sign in new int[] { -1, +1 })
{
Int128 value128 = sign * (num + k);
byte[] tempByteArray1 = GetRandomSmallByteArray(value128);
byte[] tempByteArray2 = GetRandomSmallByteArray(shift);
VerifyLeftShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b<<");
}
}
}
}
}
private static void VerifyLeftShiftString(string opstring)
{
StackCalc sc = new StackCalc(opstring);
@ -160,6 +211,19 @@ namespace System.Numerics.Tests
}
}
private static byte[] GetRandomSmallByteArray(Int128 num)
{
byte[] value = new byte[16];
for (int i = 0; i < value.Length; i++)
{
value[i] = (byte)num;
num >>= 8;
}
return value;
}
private static byte[] GetRandomByteArray(Random random)
{
return GetRandomByteArray(random, random.Next(0, 1024));

View file

@ -1,92 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Xunit;
namespace System.Numerics.Tests
{
public class op_rightshiftTest
public abstract class op_rightshiftTestBase
{
public abstract string opstring { get; }
private static int s_samples = 10;
private static Random s_random = new Random(100);
[Fact]
public static void BigShiftsTest()
public void RunRightShiftTests()
{
BigInteger a = new BigInteger(1);
BigInteger b = new BigInteger(Math.Pow(2, 31));
for (int i = 0; i < 100; i++)
{
BigInteger a1 = (a << (i + 31));
BigInteger a2 = a1 >> i;
Assert.Equal(b, a2);
}
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // May fail on 32-bit due to a large memory requirement
public static void LargeNegativeBigIntegerShiftTest()
{
// Create a very large negative BigInteger
int bitsPerElement = 8 * sizeof(uint);
int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement);
BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1);
Assert.Equal(maxBitLength - 1, bigInt.GetBitLength());
Assert.Equal(-1, bigInt.Sign);
// Validate internal representation.
// At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros.
// Given this, bigInt._bits is expected to be structured as follows:
// - _bits.Length == ceil(maxBitLength / bitsPerElement)
// - First (_bits.Length - 1) elements: 0x00000000
// - Last element: 0x80000000
// ^------ (There's the leading '1')
Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length);
uint i = 0;
for (; i < (bigInt._bits.Length - 1); i++) {
Assert.Equal(0x00000000u, bigInt._bits[i]);
}
Assert.Equal(0x80000000u, bigInt._bits[i]);
// Right shift the BigInteger
BigInteger shiftedBigInt = bigInt >> 1;
Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength());
Assert.Equal(-1, shiftedBigInt.Sign);
// Validate internal representation.
// At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros.
// Given this, shiftedBigInt._bits is expected to be structured as follows:
// - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement)
// - First (_bits.Length - 1) elements: 0x00000000
// - Last element: 0x40000000
// ^------ (the '1' is now one position to the right)
Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length);
i = 0;
for (; i < (shiftedBigInt._bits.Length - 1); i++) {
Assert.Equal(0x00000000u, shiftedBigInt._bits[i]);
}
Assert.Equal(0x40000000u, shiftedBigInt._bits[i]);
}
[Fact]
public static void RunRightShiftTests()
{
byte[] tempByteArray1 = new byte[0];
byte[] tempByteArray2 = new byte[0];
byte[] tempByteArray1;
byte[] tempByteArray2;
// RightShift Method - Large BigIntegers - large + Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = GetRandomPosByteArray(s_random, 2);
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - small + Shift
@ -94,7 +31,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - 32 bit Shift
@ -102,7 +39,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - All One Uint Large BigIntegers - 32 bit Shift
@ -110,7 +47,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomLengthAllOnesUIntByteArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Uint 0xffffffff 0x8000000 ... Large BigIntegers - 32 bit Shift
@ -118,7 +55,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(s_random);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - large - Shift
@ -126,7 +63,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = GetRandomNegByteArray(s_random, 2);
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - small - Shift
@ -134,7 +71,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - -32 bit Shift
@ -142,7 +79,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)0xe0 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Large BigIntegers - 0 bit Shift
@ -150,7 +87,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random);
tempByteArray2 = new byte[] { (byte)0 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - large + Shift
@ -158,7 +95,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = GetRandomPosByteArray(s_random, 2);
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - small + Shift
@ -166,7 +103,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - 32 bit Shift
@ -174,14 +111,14 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)32 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - large - Shift
for (int i = 0; i < s_samples; i++)
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = GetRandomNegByteArray(s_random, 2);
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - small - Shift
@ -189,7 +126,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - -32 bit Shift
@ -197,7 +134,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)0xe0 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Small BigIntegers - 0 bit Shift
@ -205,7 +142,7 @@ namespace System.Numerics.Tests
{
tempByteArray1 = GetRandomByteArray(s_random, 2);
tempByteArray2 = new byte[] { (byte)0 };
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Positive BigIntegers - Shift to 0
@ -217,7 +154,7 @@ namespace System.Numerics.Tests
{
Array.Reverse(tempByteArray2);
}
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
// RightShift Method - Negative BigIntegers - Shift to -1
@ -229,7 +166,57 @@ namespace System.Numerics.Tests
{
Array.Reverse(tempByteArray2);
}
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>");
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
}
[Fact]
public void RunSmallTests()
{
foreach (int i in new int[] {
0,
1,
16,
31,
32,
33,
63,
64,
65,
100,
127,
128,
})
{
foreach (int shift in new int[] {
0,
-1, 1,
-16, 16,
-31, 31,
-32, 32,
-33, 33,
-63, 63,
-64, 64,
-65, 65,
-100, 100,
-127, 127,
-128, 128,
})
{
var num = Int128.One << i;
for (int k = -1; k <= 1; k++)
{
foreach (int sign in new int[] { -1, +1 })
{
Int128 value128 = sign * (num + k);
byte[] tempByteArray1 = GetRandomSmallByteArray(value128);
byte[] tempByteArray2 = GetRandomSmallByteArray(shift);
VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring);
}
}
}
}
}
@ -242,6 +229,19 @@ namespace System.Numerics.Tests
}
}
private static byte[] GetRandomSmallByteArray(Int128 num)
{
byte[] value = new byte[16];
for (int i = 0; i < value.Length; i++)
{
value[i] = (byte)num;
num >>= 8;
}
return value;
}
private static byte[] GetRandomByteArray(Random random)
{
return GetRandomByteArray(random, random.Next(0, 1024));
@ -292,7 +292,7 @@ namespace System.Numerics.Tests
int gap = random.Next(0, 128);
int byteLength = 4 + gap * 4 + 1;
byte[] array = new byte[byteLength];
array[^6] = 0x80;
array[^5] = 0x80;
array[^1] = 0xFF;
return array;
}
@ -302,4 +302,93 @@ namespace System.Numerics.Tests
return MyBigIntImp.Print(bytes);
}
}
public class op_rightshiftTest : op_rightshiftTestBase
{
public override string opstring => "b>>";
[Fact]
public static void BigShiftsTest()
{
BigInteger a = new BigInteger(1);
BigInteger b = new BigInteger(Math.Pow(2, 31));
for (int i = 0; i < 100; i++)
{
BigInteger a1 = (a << (i + 31));
BigInteger a2 = a1 >> i;
Assert.Equal(b, a2);
}
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // May fail on 32-bit due to a large memory requirement
public static void LargeNegativeBigIntegerShiftTest()
{
// Create a very large negative BigInteger
int bitsPerElement = 8 * sizeof(uint);
int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement);
BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1);
Assert.Equal(maxBitLength - 1, bigInt.GetBitLength());
Assert.Equal(-1, bigInt.Sign);
// Validate internal representation.
// At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros.
// Given this, bigInt._bits is expected to be structured as follows:
// - _bits.Length == ceil(maxBitLength / bitsPerElement)
// - First (_bits.Length - 1) elements: 0x00000000
// - Last element: 0x80000000
// ^------ (There's the leading '1')
Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length);
uint i = 0;
for (; i < (bigInt._bits.Length - 1); i++)
{
Assert.Equal(0x00000000u, bigInt._bits[i]);
}
Assert.Equal(0x80000000u, bigInt._bits[i]);
// Right shift the BigInteger
BigInteger shiftedBigInt = bigInt >> 1;
Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength());
Assert.Equal(-1, shiftedBigInt.Sign);
// Validate internal representation.
// At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros.
// Given this, shiftedBigInt._bits is expected to be structured as follows:
// - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement)
// - First (_bits.Length - 1) elements: 0x00000000
// - Last element: 0x40000000
// ^------ (the '1' is now one position to the right)
Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length);
i = 0;
for (; i < (shiftedBigInt._bits.Length - 1); i++)
{
Assert.Equal(0x00000000u, shiftedBigInt._bits[i]);
}
Assert.Equal(0x40000000u, shiftedBigInt._bits[i]);
}
}
public class op_UnsignedRightshiftTest : op_rightshiftTestBase
{
public override string opstring => "b>>>";
[Fact]
public void PowerOfTwo()
{
for (int i = 0; i < 32; i++)
{
foreach (int k in new int[] { 1, 2, 10 })
{
Assert.Equal(BigInteger.One << i, (BigInteger.One << (32 * k + i)) >>> (32 * k));
Assert.Equal(new BigInteger(unchecked((int)(uint.MaxValue << i))), (BigInteger.MinusOne << (32 * k + i)) >>> (32 * k));
}
}
}
}
}

View file

@ -191,8 +191,14 @@ namespace System.Numerics.Tests
return BigInteger.Max(num1, num2);
case "b>>":
return num1 >> (int)num2;
case "b>>>":
return num1 >>> (int)num2;
case "b<<":
return num1 << (int)num2;
case "bRotateLeft":
return BigInteger.RotateLeft(num1, (int)num2);
case "bRotateRight":
return BigInteger.RotateRight(num1, (int)num2);
case "b^":
return num1 ^ num2;
case "b|":
@ -254,7 +260,7 @@ namespace System.Numerics.Tests
private static string Print(byte[] bytes)
{
return MyBigIntImp.PrintFormatX(bytes);
return MyBigIntImp.PrintFormatX(bytes);
}
}
}

View file

@ -42,6 +42,7 @@
<Compile Include="BigInteger\op_multiply.cs" />
<Compile Include="BigInteger\op_not.cs" />
<Compile Include="BigInteger\op_or.cs" />
<Compile Include="BigInteger\Rotate.cs" />
<Compile Include="BigInteger\op_rightshift.cs" />
<Compile Include="BigInteger\op_xor.cs" />
<Compile Include="BigInteger\parse.cs" />