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 5f4cf82f75a941..539e1c3175955b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1707,7 +1707,7 @@ private static BigInteger Add(ReadOnlySpan leftBits, int leftSign, ReadOnl } if (bitsFromPool != null) - ArrayPool.Shared.Return(bitsFromPool); + ArrayPool.Shared.Return(bitsFromPool); return result; } @@ -2642,7 +2642,7 @@ public static implicit operator BigInteger(nuint value) if (zdFromPool != null) ArrayPool.Shared.Return(zdFromPool); - exit: + exit: if (xdFromPool != null) ArrayPool.Shared.Return(xdFromPool); @@ -3255,7 +3255,27 @@ 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); + + 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)); @@ -3272,14 +3292,13 @@ public static BigInteger RotateLeft(BigInteger value, int 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); + xd[^1] = 0; - bool negx = value.GetPartsForBitManipulation(xd); + bits.CopyTo(xd); int zl = xl; uint[]? zdFromPool = null; @@ -3390,7 +3409,28 @@ public static BigInteger RotateLeft(BigInteger value, int rotateAmount) 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)); @@ -3407,14 +3447,13 @@ public static BigInteger RotateRight(BigInteger value, int 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); + xd[^1] = 0; - bool negx = value.GetPartsForBitManipulation(xd); + bits.CopyTo(xd); int zl = xl; uint[]? zdFromPool = null; @@ -3461,19 +3500,12 @@ public static BigInteger RotateRight(BigInteger value, int rotateAmount) { 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 { @@ -3482,22 +3514,22 @@ public static BigInteger RotateRight(BigInteger value, int rotateAmount) 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--; } } @@ -5252,13 +5284,32 @@ static bool INumberBase.TryConvertToTruncating(BigInteger va 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 cd4a578fab910e..8bb69d9f5e25bd 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 @@ public static BigInteger DoBinaryOperatorMine(BigInteger num1, BigInteger num2, 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 @@ public static List Not(List bytes) 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 @@ 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..564eb23935cda9 --- /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 47b0a8ea7678cd..6c278bb63edb6e 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 @@ public static void RunLeftShiftTests() } } + [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 @@ private static void VerifyLeftShiftString(string opstring) } } + 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 da6ac2615b9c1b..c725d52bc0ba4c 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -1,90 +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() - { - 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 - BigInteger bigInt = new BigInteger(-1) << int.MaxValue; - Assert.Equal(2147483647, bigInt.GetBitLength()); - Assert.Equal(-1, bigInt.Sign); - - // Validate internal representation. - // At this point, bigInt should be a 1 followed by int.MaxValue zeros. - // Given this, bigInt._bits is expected to be structured as follows: - // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) - // - First (_bits.Length - 1) elements: 0x00000000 - // - Last element: 0x80000000 - // ^------ (There's the leading '1') - - Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)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(2147483646, shiftedBigInt.GetBitLength()); - Assert.Equal(-1, shiftedBigInt.Sign); - - // Validate internal representation. - // At this point, shiftedBigInt should be a 1 followed by int.MaxValue - 1 zeros. - // Given this, shiftedBigInt._bits is expected to be structured as follows: - // - _bits.Length == (int.MaxValue + 1) / (8 * sizeof(uint)) - // - First (_bits.Length - 1) elements: 0x00000000 - // - Last element: 0x40000000 - // ^------ (the '1' is now one position to the right) - - Assert.Equal(((uint)int.MaxValue + 1) / (8 * sizeof(uint)), (uint)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() + public 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 @@ -92,7 +31,7 @@ public static void RunRightShiftTests() { 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 @@ -100,7 +39,7 @@ public static void RunRightShiftTests() { 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 @@ -108,7 +47,7 @@ public static void RunRightShiftTests() { 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 @@ -116,7 +55,7 @@ public static void RunRightShiftTests() { 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 @@ -124,7 +63,7 @@ public static void RunRightShiftTests() { 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 @@ -132,7 +71,7 @@ public static void RunRightShiftTests() { 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 @@ -140,7 +79,7 @@ public static void RunRightShiftTests() { 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 @@ -148,7 +87,7 @@ public static void RunRightShiftTests() { 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 @@ -156,7 +95,7 @@ public static void RunRightShiftTests() { 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 @@ -164,7 +103,7 @@ public static void RunRightShiftTests() { 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 @@ -172,14 +111,14 @@ public static void RunRightShiftTests() { 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 @@ -187,7 +126,7 @@ public static void RunRightShiftTests() { 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 @@ -195,7 +134,7 @@ public static void RunRightShiftTests() { 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 @@ -203,7 +142,7 @@ public static void RunRightShiftTests() { 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 @@ -215,7 +154,7 @@ public static void RunRightShiftTests() { Array.Reverse(tempByteArray2); } - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Negative BigIntegers - Shift to -1 @@ -227,7 +166,57 @@ public static void RunRightShiftTests() { 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); + } + } + } } } @@ -240,6 +229,19 @@ private static void VerifyRightShiftString(string opstring) } } + 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)); @@ -290,7 +292,7 @@ private static byte[] GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(Random ra 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; } @@ -300,4 +302,93 @@ private static string Print(byte[] bytes) 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 c53e87a15a234f..cbd2a04127ef7f 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 @@ private BigInteger DoBinaryOperatorSN(BigInteger num1, BigInteger num2, string o 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 @@ 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 0716f70c88e651..49ded253d98507 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 @@ -43,6 +43,7 @@ +