Skip to content

Commit 20bca5c

Browse files
committed
Fix BigInteger.Rotate{Left,Right}
1 parent 2a51ee3 commit 20bca5c

File tree

8 files changed

+668
-233
lines changed

8 files changed

+668
-233
lines changed

src/libraries/System.Runtime.Numerics/src/System.Runtime.Numerics.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<Compile Include="System\Numerics\BigIntegerCalculator.GcdInv.cs" />
1616
<Compile Include="System\Numerics\BigIntegerCalculator.PowMod.cs" />
1717
<Compile Include="System\Numerics\BigIntegerCalculator.SquMul.cs" />
18+
<Compile Include="System\Numerics\BigIntegerCalculator.ShiftRot.cs" />
1819
<Compile Include="System\Numerics\BigIntegerCalculator.Utils.cs" />
1920
<Compile Include="System\Numerics\BigInteger.cs" />
2021
<Compile Include="System\Number.BigInteger.cs" />

src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs

Lines changed: 46 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ private static BigInteger Add(ReadOnlySpan<uint> leftBits, int leftSign, ReadOnl
17011701
}
17021702

17031703
if (bitsFromPool != null)
1704-
ArrayPool<uint>.Shared.Return(bitsFromPool);
1704+
ArrayPool<uint>.Shared.Return(bitsFromPool);
17051705

17061706
return result;
17071707
}
@@ -2636,7 +2636,7 @@ public static implicit operator BigInteger(nuint value)
26362636

26372637
if (zdFromPool != null)
26382638
ArrayPool<uint>.Shared.Return(zdFromPool);
2639-
exit:
2639+
exit:
26402640
if (xdFromPool != null)
26412641
ArrayPool<uint>.Shared.Return(xdFromPool);
26422642

@@ -3227,7 +3227,6 @@ public static BigInteger PopCount(BigInteger value)
32273227

32283228
part = ~value._bits[i];
32293229
result += uint.PopCount(part);
3230-
32313230
i++;
32323231
}
32333232
}
@@ -3239,267 +3238,89 @@ public static BigInteger PopCount(BigInteger value)
32393238
public static BigInteger RotateLeft(BigInteger value, int rotateAmount)
32403239
{
32413240
value.AssertValid();
3242-
int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
3243-
3244-
// Normalize the rotate amount to drop full rotations
3245-
rotateAmount = (int)(rotateAmount % (byteCount * 8L));
32463241

32473242
if (rotateAmount == 0)
32483243
return value;
32493244

3250-
if (rotateAmount == int.MinValue)
3251-
return RotateRight(RotateRight(value, int.MaxValue), 1);
3252-
3253-
if (rotateAmount < 0)
3254-
return RotateRight(value, -rotateAmount);
3255-
3256-
(int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
3257-
3258-
uint[]? xdFromPool = null;
3259-
int xl = value._bits?.Length ?? 1;
3260-
3261-
Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
3262-
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
3263-
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
3264-
xd = xd.Slice(0, xl);
3265-
3266-
bool negx = value.GetPartsForBitManipulation(xd);
3267-
3268-
int zl = xl;
3269-
uint[]? zdFromPool = null;
3270-
3271-
Span<uint> zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
3272-
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
3273-
: zdFromPool = ArrayPool<uint>.Shared.Rent(zl);
3274-
zd = zd.Slice(0, zl);
3275-
3276-
zd.Clear();
3277-
3278-
if (negx)
3279-
{
3280-
NumericsHelpers.DangerousMakeTwosComplement(xd);
3281-
}
3282-
3283-
if (smallShift == 0)
3284-
{
3285-
int dstIndex = 0;
3286-
int srcIndex = xd.Length - digitShift;
3287-
3288-
do
3289-
{
3290-
// Copy last digitShift elements from xd to the start of zd
3291-
zd[dstIndex] = xd[srcIndex];
3292-
3293-
dstIndex++;
3294-
srcIndex++;
3295-
}
3296-
while (srcIndex < xd.Length);
3297-
3298-
srcIndex = 0;
3299-
3300-
while (dstIndex < zd.Length)
3301-
{
3302-
// Copy remaining elements from start of xd to end of zd
3303-
zd[dstIndex] = xd[srcIndex];
3245+
bool neg = value._sign < 0;
33043246

3305-
dstIndex++;
3306-
srcIndex++;
3307-
}
3308-
}
3309-
else
3310-
{
3311-
int carryShift = kcbitUint - smallShift;
3312-
3313-
int dstIndex = 0;
3314-
int srcIndex = 0;
3315-
3316-
uint carry = 0;
3317-
3318-
if (digitShift == 0)
3319-
{
3320-
carry = xd[^1] >> carryShift;
3321-
}
3322-
else
3323-
{
3324-
srcIndex = xd.Length - digitShift;
3325-
carry = xd[srcIndex - 1] >> carryShift;
3326-
}
3327-
3328-
do
3329-
{
3330-
uint part = xd[srcIndex];
3331-
3332-
zd[dstIndex] = (part << smallShift) | carry;
3333-
carry = part >> carryShift;
3334-
3335-
dstIndex++;
3336-
srcIndex++;
3337-
}
3338-
while (srcIndex < xd.Length);
3339-
3340-
srcIndex = 0;
3341-
3342-
while (dstIndex < zd.Length)
3343-
{
3344-
uint part = xd[srcIndex];
3345-
3346-
zd[dstIndex] = (part << smallShift) | carry;
3347-
carry = part >> carryShift;
3348-
3349-
dstIndex++;
3350-
srcIndex++;
3351-
}
3352-
}
3353-
3354-
if (negx && (int)zd[^1] < 0)
3355-
{
3356-
NumericsHelpers.DangerousMakeTwosComplement(zd);
3357-
}
3358-
else
3247+
if (value._bits is null)
33593248
{
3360-
negx = false;
3249+
uint rs = BitOperations.RotateLeft((uint)value._sign, rotateAmount);
3250+
return neg
3251+
? new BigInteger((int)rs)
3252+
: new BigInteger(rs);
33613253
}
33623254

3363-
var result = new BigInteger(zd, negx);
3364-
3365-
if (xdFromPool != null)
3366-
ArrayPool<uint>.Shared.Return(xdFromPool);
3367-
if (zdFromPool != null)
3368-
ArrayPool<uint>.Shared.Return(zdFromPool);
3369-
3370-
return result;
3255+
return Rotate(value._bits, neg, rotateAmount);
33713256
}
33723257

33733258
/// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" />
33743259
public static BigInteger RotateRight(BigInteger value, int rotateAmount)
33753260
{
33763261
value.AssertValid();
3377-
int byteCount = (value._bits is null) ? sizeof(int) : (value._bits.Length * 4);
3378-
3379-
// Normalize the rotate amount to drop full rotations
3380-
rotateAmount = (int)(rotateAmount % (byteCount * 8L));
33813262

33823263
if (rotateAmount == 0)
33833264
return value;
33843265

3385-
if (rotateAmount == int.MinValue)
3386-
return RotateLeft(RotateLeft(value, int.MaxValue), 1);
3387-
3388-
if (rotateAmount < 0)
3389-
return RotateLeft(value, -rotateAmount);
3390-
3391-
(int digitShift, int smallShift) = Math.DivRem(rotateAmount, kcbitUint);
3266+
bool neg = value._sign < 0;
33923267

3393-
uint[]? xdFromPool = null;
3394-
int xl = value._bits?.Length ?? 1;
3395-
3396-
Span<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold)
3397-
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
3398-
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl);
3399-
xd = xd.Slice(0, xl);
3400-
3401-
bool negx = value.GetPartsForBitManipulation(xd);
3402-
3403-
int zl = xl;
3404-
uint[]? zdFromPool = null;
3405-
3406-
Span<uint> zd = (zl <= BigIntegerCalculator.StackAllocThreshold)
3407-
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
3408-
: zdFromPool = ArrayPool<uint>.Shared.Rent(zl);
3409-
zd = zd.Slice(0, zl);
3410-
3411-
zd.Clear();
3412-
3413-
if (negx)
3268+
if (value._bits is null)
34143269
{
3415-
NumericsHelpers.DangerousMakeTwosComplement(xd);
3270+
uint rs = BitOperations.RotateRight((uint)value._sign, rotateAmount);
3271+
return neg
3272+
? new BigInteger((int)rs)
3273+
: new BigInteger(rs);
34163274
}
34173275

3418-
if (smallShift == 0)
3419-
{
3420-
int dstIndex = 0;
3421-
int srcIndex = digitShift;
3422-
3423-
do
3424-
{
3425-
// Copy first digitShift elements from xd to the end of zd
3426-
zd[dstIndex] = xd[srcIndex];
3427-
3428-
dstIndex++;
3429-
srcIndex++;
3430-
}
3431-
while (srcIndex < xd.Length);
3432-
3433-
srcIndex = 0;
3434-
3435-
while (dstIndex < zd.Length)
3436-
{
3437-
// Copy remaining elements from end of xd to start of zd
3438-
zd[dstIndex] = xd[srcIndex];
3439-
3440-
dstIndex++;
3441-
srcIndex++;
3442-
}
3443-
}
3444-
else
3445-
{
3446-
int carryShift = kcbitUint - smallShift;
3447-
3448-
int dstIndex = 0;
3449-
int srcIndex = digitShift;
3450-
3451-
uint carry = 0;
3452-
3453-
if (digitShift == 0)
3454-
{
3455-
carry = xd[^1] << carryShift;
3456-
}
3457-
else
3458-
{
3459-
carry = xd[srcIndex - 1] << carryShift;
3460-
}
3276+
return Rotate(value._bits, neg, -(long)rotateAmount);
3277+
}
34613278

3462-
do
3463-
{
3464-
uint part = xd[srcIndex];
3279+
private static BigInteger Rotate(ReadOnlySpan<uint> bits, bool negative, long rotateLeftAmount)
3280+
{
3281+
Debug.Assert(bits.Length > 0);
3282+
Debug.Assert(Math.Abs(rotateLeftAmount) <= 0x80000000);
34653283

3466-
zd[dstIndex] = (part >> smallShift) | carry;
3467-
carry = part << carryShift;
3284+
int zLength = bits.Length;
3285+
int leadingZeroCount = negative ? bits.IndexOfAnyExcept(0u) : 0;
34683286

3469-
dstIndex++;
3470-
srcIndex++;
3471-
}
3472-
while (srcIndex < xd.Length);
3287+
if (negative && bits[^1] >= kuMaskHighBit
3288+
&& !(leadingZeroCount == bits.Length - 1 && bits[^1] == kuMaskHighBit))
3289+
++zLength;
34733290

3474-
srcIndex = 0;
3291+
uint[]? zFromPool = null;
3292+
Span<uint> zd = ((uint)zLength <= BigIntegerCalculator.StackAllocThreshold
3293+
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
3294+
: zFromPool = ArrayPool<uint>.Shared.Rent(zLength)).Slice(0, zLength);
34753295

3476-
while (dstIndex < zd.Length)
3477-
{
3478-
uint part = xd[srcIndex];
3296+
zd[^1] = 0;
3297+
bits.CopyTo(zd);
34793298

3480-
zd[dstIndex] = (part >> smallShift) | carry;
3481-
carry = part << carryShift;
3299+
if (negative)
3300+
{
3301+
Debug.Assert((uint)leadingZeroCount < (uint)zd.Length);
34823302

3483-
dstIndex++;
3484-
srcIndex++;
3485-
}
3303+
// Same as NumericsHelpers.DangerousMakeTwosComplement(zd);
3304+
// Leading zero count are already calculated.
3305+
zd[leadingZeroCount] = (uint)(-(int)zd[leadingZeroCount]);
3306+
NumericsHelpers.DangerousMakeOnesComplement(zd.Slice(leadingZeroCount + 1));
34863307
}
34873308

3488-
if (negx && (int)zd[^1] < 0)
3309+
BigIntegerCalculator.RotateLeft(zd, rotateLeftAmount);
3310+
3311+
if (negative && (int)zd[^1] < 0)
34893312
{
34903313
NumericsHelpers.DangerousMakeTwosComplement(zd);
34913314
}
34923315
else
34933316
{
3494-
negx = false;
3317+
negative = false;
34953318
}
34963319

3497-
var result = new BigInteger(zd, negx);
3320+
var result = new BigInteger(zd, negative);
34983321

3499-
if (xdFromPool != null)
3500-
ArrayPool<uint>.Shared.Return(xdFromPool);
3501-
if (zdFromPool != null)
3502-
ArrayPool<uint>.Shared.Return(zdFromPool);
3322+
if (zFromPool != null)
3323+
ArrayPool<uint>.Shared.Return(zFromPool);
35033324

35043325
return result;
35053326
}

0 commit comments

Comments
 (0)