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 @@ +