diff --git a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj
index 6526656921c343..52a68d2a73475c 100644
--- a/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj
+++ b/src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
index 418a565dbd812e..695c79d73e7808 100644
--- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
@@ -1701,7 +1701,7 @@ private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnl
}
if (bitsFromPool != null)
- ArrayPool.Shared.Return(bitsFromPool);
+ ArrayPool.Shared.Return(bitsFromPool);
return result;
}
@@ -2636,7 +2636,7 @@ public static implicit operator BigInteger(nuint value)
if (zdFromPool != null)
ArrayPool.Shared.Return(zdFromPool);
- exit:
+ exit:
if (xdFromPool != null)
ArrayPool.Shared.Return(xdFromPool);
@@ -3227,7 +3227,6 @@ public static BigInteger PopCount(BigInteger value)
part = ~value._bits[i];
result += uint.PopCount(part);
-
i++;
}
}
@@ -3239,267 +3238,89 @@ public static BigInteger PopCount(BigInteger value)
public static BigInteger RotateLeft(BigInteger value, int rotateAmount)
{
value.AssertValid();
- int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
-
- // Normalize the rotate amount to drop full rotations
- rotateAmount = (int)(rotateAmount % (byteCount * 8L));
if (rotateAmount == 0)
return value;
- if (rotateAmount == int.MinValue)
- return RotateRight(RotateRight(value, int.MaxValue), 1);
-
- if (rotateAmount < 0)
- return RotateRight(value, -rotateAmount);
-
- (int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
-
- uint[]? xdFromPool = null;
- int xl = value._bits?.Length ?? 1;
-
- Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
- ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
- : xdFromPool = ArrayPool.Shared.Rent(xl);
- xd = xd.Slice(0, xl);
-
- bool negx = value.GetPartsForBitManipulation(xd);
-
- int zl = xl;
- uint[]? zdFromPool = null;
-
- Span zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
- ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
- : zdFromPool = ArrayPool.Shared.Rent(zl);
- zd = zd.Slice(0, zl);
-
- zd.Clear();
-
- if (negx)
- {
- NumericsHelpers.DangerousMakeTwosComplement(xd);
- }
-
- if (smallShift == 0)
- {
- int dstIndex = 0;
- int srcIndex = xd.Length - digitShift;
-
- do
- {
- // Copy last digitShift elements from xd to the start of zd
- zd[dstIndex] = xd[srcIndex];
-
- dstIndex++;
- srcIndex++;
- }
- while (srcIndex < xd.Length);
-
- srcIndex = 0;
-
- while (dstIndex < zd.Length)
- {
- // Copy remaining elements from start of xd to end of zd
- zd[dstIndex] = xd[srcIndex];
+ bool neg = value._sign < 0;
- dstIndex++;
- srcIndex++;
- }
- }
- else
- {
- int carryShift = kcbitUint - smallShift;
-
- int dstIndex = 0;
- int srcIndex = 0;
-
- uint carry = 0;
-
- if (digitShift == 0)
- {
- carry = xd[^1] >> carryShift;
- }
- else
- {
- srcIndex = xd.Length - digitShift;
- carry = xd[srcIndex - 1] >> carryShift;
- }
-
- do
- {
- uint part = xd[srcIndex];
-
- zd[dstIndex] = (part << smallShift) | carry;
- carry = part >> carryShift;
-
- dstIndex++;
- srcIndex++;
- }
- while (srcIndex < xd.Length);
-
- srcIndex = 0;
-
- while (dstIndex < zd.Length)
- {
- uint part = xd[srcIndex];
-
- zd[dstIndex] = (part << smallShift) | carry;
- carry = part >> carryShift;
-
- dstIndex++;
- srcIndex++;
- }
- }
-
- if (negx && (int)zd[^1] < 0)
- {
- NumericsHelpers.DangerousMakeTwosComplement(zd);
- }
- else
+ if (value._bits is null)
{
- negx = false;
+ uint rs = BitOperations.RotateLeft((uint)value._sign, rotateAmount);
+ return neg
+ ? new BigInteger((int)rs)
+ : new BigInteger(rs);
}
- var result = new BigInteger(zd, negx);
-
- if (xdFromPool != null)
- ArrayPool.Shared.Return(xdFromPool);
- if (zdFromPool != null)
- ArrayPool.Shared.Return(zdFromPool);
-
- return result;
+ return Rotate(value._bits, neg, rotateAmount);
}
///
public static BigInteger RotateRight(BigInteger value, int rotateAmount)
{
value.AssertValid();
- int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
-
- // Normalize the rotate amount to drop full rotations
- rotateAmount = (int)(rotateAmount % (byteCount * 8L));
if (rotateAmount == 0)
return value;
- if (rotateAmount == int.MinValue)
- return RotateLeft(RotateLeft(value, int.MaxValue), 1);
-
- if (rotateAmount < 0)
- return RotateLeft(value, -rotateAmount);
-
- (int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
+ bool neg = value._sign < 0;
- uint[]? xdFromPool = null;
- int xl = value._bits?.Length ?? 1;
-
- Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
- ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
- : xdFromPool = ArrayPool.Shared.Rent(xl);
- xd = xd.Slice(0, xl);
-
- bool negx = value.GetPartsForBitManipulation(xd);
-
- int zl = xl;
- uint[]? zdFromPool = null;
-
- Span zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
- ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
- : zdFromPool = ArrayPool.Shared.Rent(zl);
- zd = zd.Slice(0, zl);
-
- zd.Clear();
-
- if (negx)
+ if (value._bits is null)
{
- NumericsHelpers.DangerousMakeTwosComplement(xd);
+ uint rs = BitOperations.RotateRight((uint)value._sign, rotateAmount);
+ return neg
+ ? new BigInteger((int)rs)
+ : new BigInteger(rs);
}
- if (smallShift == 0)
- {
- int dstIndex = 0;
- int srcIndex = digitShift;
-
- do
- {
- // Copy first digitShift elements from xd to the end of zd
- zd[dstIndex] = xd[srcIndex];
-
- dstIndex++;
- srcIndex++;
- }
- while (srcIndex < xd.Length);
-
- srcIndex = 0;
-
- while (dstIndex < zd.Length)
- {
- // Copy remaining elements from end of xd to start of zd
- zd[dstIndex] = xd[srcIndex];
-
- dstIndex++;
- srcIndex++;
- }
- }
- else
- {
- int carryShift = kcbitUint - smallShift;
-
- int dstIndex = 0;
- int srcIndex = digitShift;
-
- uint carry = 0;
-
- if (digitShift == 0)
- {
- carry = xd[^1] << carryShift;
- }
- else
- {
- carry = xd[srcIndex - 1] << carryShift;
- }
+ return Rotate(value._bits, neg, -(long)rotateAmount);
+ }
- do
- {
- uint part = xd[srcIndex];
+ private static BigInteger Rotate(ReadOnlySpan bits, bool negative, long rotateLeftAmount)
+ {
+ Debug.Assert(bits.Length > 0);
+ Debug.Assert(Math.Abs(rotateLeftAmount) <= 0x80000000);
- zd[dstIndex] = (part >> smallShift) | carry;
- carry = part << carryShift;
+ int zLength = bits.Length;
+ int leadingZeroCount = negative ? bits.IndexOfAnyExcept(0u) : 0;
- dstIndex++;
- srcIndex++;
- }
- while (srcIndex < xd.Length);
+ if (negative && bits[^1] >= kuMaskHighBit
+ && !(leadingZeroCount == bits.Length - 1 && bits[^1] == kuMaskHighBit))
+ ++zLength;
- srcIndex = 0;
+ uint[]? zFromPool = null;
+ Span zd = ((uint)zLength <= BigIntegerCalculator.StackAllocThreshold
+ ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
+ : zFromPool = ArrayPool.Shared.Rent(zLength)).Slice(0, zLength);
- while (dstIndex < zd.Length)
- {
- uint part = xd[srcIndex];
+ zd[^1] = 0;
+ bits.CopyTo(zd);
- zd[dstIndex] = (part >> smallShift) | carry;
- carry = part << carryShift;
+ if (negative)
+ {
+ Debug.Assert((uint)leadingZeroCount < (uint)zd.Length);
- dstIndex++;
- srcIndex++;
- }
+ // Same as NumericsHelpers.DangerousMakeTwosComplement(zd);
+ // Leading zero count is already calculated.
+ zd[leadingZeroCount] = (uint)(-(int)zd[leadingZeroCount]);
+ NumericsHelpers.DangerousMakeOnesComplement(zd.Slice(leadingZeroCount + 1));
}
- if (negx && (int)zd[^1] < 0)
+ BigIntegerCalculator.RotateLeft(zd, rotateLeftAmount);
+
+ if (negative && (int)zd[^1] < 0)
{
NumericsHelpers.DangerousMakeTwosComplement(zd);
}
else
{
- negx = false;
+ negative = false;
}
- var result = new BigInteger(zd, negx);
+ var result = new BigInteger(zd, negative);
- if (xdFromPool != null)
- ArrayPool.Shared.Return(xdFromPool);
- if (zdFromPool != null)
- ArrayPool.Shared.Return(zdFromPool);
+ if (zFromPool != null)
+ ArrayPool.Shared.Return(zFromPool);
return result;
}
diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.ShiftRot.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.ShiftRot.cs
new file mode 100644
index 00000000000000..7282ca8c36b77b
--- /dev/null
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.ShiftRot.cs
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Numerics
+{
+ internal static partial class BigIntegerCalculator
+ {
+ public static void RotateLeft(Span bits, long rotateLeftAmount)
+ {
+ Debug.Assert(Math.Abs(rotateLeftAmount) <= 0x80000000);
+
+ const int digitShiftMax = (int)(0x80000000 / 32);
+
+ int digitShift = digitShiftMax;
+ int smallShift = 0;
+
+ if (rotateLeftAmount < 0)
+ {
+ if (rotateLeftAmount != -0x80000000)
+ (digitShift, smallShift) = Math.DivRem(-(int)rotateLeftAmount, 32);
+
+ RotateRight(bits, digitShift % bits.Length, smallShift);
+ }
+ else
+ {
+ if (rotateLeftAmount != 0x80000000)
+ (digitShift, smallShift) = Math.DivRem((int)rotateLeftAmount, 32);
+
+ RotateLeft(bits, digitShift % bits.Length, smallShift);
+ }
+ }
+
+ public static void RotateLeft(Span bits, int digitShift, int smallShift)
+ {
+ Debug.Assert(bits.Length > 0);
+
+ LeftShiftSelf(bits, smallShift, out uint carry);
+ bits[0] |= carry;
+
+ if (digitShift == 0)
+ return;
+
+ SwapUpperAndLower(bits, bits.Length - digitShift);
+ }
+
+ public static void RotateRight(Span bits, int digitShift, int smallShift)
+ {
+ Debug.Assert(bits.Length > 0);
+
+ RightShiftSelf(bits, smallShift, out uint carry);
+ bits[^1] |= carry;
+
+ if (digitShift == 0)
+ return;
+
+ SwapUpperAndLower(bits, digitShift);
+ }
+
+ private static void SwapUpperAndLower(Span bits, int lowerLength)
+ {
+ Debug.Assert(lowerLength > 0);
+ Debug.Assert(lowerLength < bits.Length);
+
+ int upperLength = bits.Length - lowerLength;
+
+ Span lower = bits.Slice(0, lowerLength);
+ Span upper = bits.Slice(lowerLength);
+
+ Span lowerDst = bits.Slice(upperLength);
+
+ int tmpLength = Math.Min(lowerLength, upperLength);
+ uint[]? tmpFromPool = null;
+ Span tmp = ((uint)tmpLength <= StackAllocThreshold ?
+ stackalloc uint[StackAllocThreshold]
+ : tmpFromPool = ArrayPool.Shared.Rent(tmpLength)).Slice(0, tmpLength);
+
+ if (upperLength < lowerLength)
+ {
+ upper.CopyTo(tmp);
+ lower.CopyTo(lowerDst);
+ tmp.CopyTo(bits);
+ }
+ else
+ {
+ lower.CopyTo(tmp);
+ upper.CopyTo(bits);
+ tmp.CopyTo(lowerDst);
+ }
+
+ if (tmpFromPool != null)
+ ArrayPool.Shared.Return(tmpFromPool);
+ }
+
+ public static void LeftShiftSelf(Span bits, int shift, out uint carry)
+ {
+ Debug.Assert((uint)shift < 32);
+
+ carry = 0;
+ if (shift == 0)
+ return;
+
+ int back = 32 - shift;
+ for (int i = 0; i < bits.Length; i++)
+ {
+ uint value = carry | bits[i] << shift;
+ carry = bits[i] >> back;
+ bits[i] = value;
+ }
+ }
+ public static void RightShiftSelf(Span bits, int shift, out uint carry)
+ {
+ Debug.Assert((uint)shift < 32);
+
+ carry = 0;
+ if (shift == 0)
+ return;
+
+ int back = 32 - shift;
+ for (int i = bits.Length - 1; i >= 0; i--)
+ {
+ uint value = carry | bits[i] >> shift;
+ carry = bits[i] << back;
+ bits[i] = value;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs
index 6190f14433e333..687cf52e894343 100644
--- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs
+++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -107,23 +108,37 @@ public static void DangerousMakeTwosComplement(Span d)
// where A = ~X and B = -Y
// Trim trailing 0s (at the first in little endian array)
- d = d.TrimStart(0u);
+ int i = d.IndexOfAnyExcept(0u);
- // Make the first non-zero element to be two's complement
- if (d.Length > 0)
+ if ((uint)i >= (uint)d.Length)
{
- d[0] = (uint)(-(int)d[0]);
- d = d.Slice(1);
+ return;
}
+ // Make the first non-zero element to be two's complement
+ d[i] = (uint)(-(int)d[i]);
+ d = d.Slice(i + 1);
+
if (d.IsEmpty)
{
return;
}
- // Make one's complement for other elements
- int offset = 0;
+ DangerousMakeOnesComplement(d);
+ }
+ // Do an in-place one's complement. "Dangerous" because it causes
+ // a mutation and needs to be used with care for immutable types.
+ public static void DangerousMakeOnesComplement(Span d)
+ {
+ // Given a number:
+ // XXXXXXXXXXX
+ // where Y is non-zero,
+ // The result of one's complement is
+ // AAAAAAAAAAA
+ // where A = ~X
+
+ int offset = 0;
ref uint start = ref MemoryMarshal.GetReference(d);
while (Vector512.IsHardwareAccelerated && d.Length - offset >= Vector512.Count)
diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs
index 58d4afdc819f71..96699257a4133d 100644
--- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs
+++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs
@@ -110,6 +110,10 @@ public static BigInteger DoBinaryOperatorMine(BigInteger num1, BigInteger num2,
return new BigInteger(ShiftLeft(bytes1, Negate(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|":
@@ -774,6 +778,105 @@ public static List ShiftRight(List bytes)
return bresult;
}
+ public static List RotateRight(List bytes)
+ {
+ List bresult = new List();
+
+ 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 RotateLeft(List bytes)
+ {
+ List bresult = new List();
+
+ 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 RotateLeft(List bytes1, List bytes2)
+ {
+ List bytes1Copy = Copy(bytes1);
+ int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List(new byte[] { 8 })).ToArray());
+ sbyte bitShift = (sbyte)new BigInteger(Remainder(bytes2, new List(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 temp = new List();
+ 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 SetLength(List bytes, int size)
{
List bresult = new List();
diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/Rotate.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/Rotate.cs
new file mode 100644
index 00000000000000..237c76a77e5915
--- /dev/null
+++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/Rotate.cs
@@ -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 NegativeNumber_TestData = new TheoryData
+ {
+
+ {
+ 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 NegativeNumber_TestData = new TheoryData
+ {
+
+ {
+ 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));
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs
index c53e87a15a234f..7160fd0f0582d4 100644
--- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs
+++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs
@@ -193,6 +193,10 @@ private BigInteger DoBinaryOperatorSN(BigInteger num1, BigInteger num2, string o
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 +258,7 @@ public void VerifyOutParameter()
private static string Print(byte[] bytes)
{
- return MyBigIntImp.PrintFormatX(bytes);
+ return MyBigIntImp.PrintFormatX(bytes);
}
}
}
diff --git a/src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj b/src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj
index 4224f0ac83414f..5d468f9ce66885 100644
--- a/src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj
+++ b/src/libraries/System.Runtime.Numerics/tests/System.Runtime.Numerics.Tests.csproj
@@ -42,6 +42,7 @@
+