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 b3355ff6867..e173705da39 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1694,7 +1694,7 @@ namespace System.Numerics } if (bitsFromPool != null) - ArrayPool.Shared.Return(bitsFromPool); + ArrayPool.Shared.Return(bitsFromPool); return result; } @@ -2629,7 +2629,7 @@ namespace System.Numerics if (zdFromPool != null) ArrayPool.Shared.Return(zdFromPool); - exit: + exit: if (xdFromPool != null) ArrayPool.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 bits = value._bits; + if (bits.IsEmpty) + { + bits = new ReadOnlySpan(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 xd = (xl <= BigIntegerCalculator.StackAllocThreshold) ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : xdFromPool = ArrayPool.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 bits = value._bits; + if (bits.IsEmpty) + { + bits = new ReadOnlySpan(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 xd = (xl <= BigIntegerCalculator.StackAllocThreshold) ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : xdFromPool = ArrayPool.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 bits = value._bits; + if (bits.IsEmpty) + { + bits = new ReadOnlySpan(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 xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); - - bool negx = value.GetPartsForBitManipulation(xd); + xd[^1] = 0; + bits.CopyTo(xd); if (negx) { diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs index 58d4afdc819..2646d85e8df 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs @@ -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 ShiftRightUnsigned(List bytes1, List bytes2) + { + int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List(new byte[] { 8 })).ToArray()); + sbyte bitShift = (sbyte)new BigInteger(Remainder(Copy(bytes2), new List(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 temp = new List(); + 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 ShiftLeft(List bytes1, List bytes2) { 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); + 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 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 00000000000..564eb23935c --- /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/op_leftshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs index 47b0a8ea767..6c278bb63ed 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs @@ -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)); diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs index 404902f6197..c725d52bc0b 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -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)); + } + } + } + } } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs index c53e87a15a2..cbd2a04127e 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs @@ -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); } } } 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 4224f0ac834..5d468f9ce66 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 @@ +