Skip to content

Commit

Permalink
Optimize BigInteger.CompareTo() (dotnet#96835)
Browse files Browse the repository at this point in the history
* Improve BigInteger.CompareTo()

* Add test cases

* Safe

* Remove unused usings

* Update assertion
  • Loading branch information
kzrnm authored Jan 29, 2024
1 parent 678ae31 commit 9bbfa9a
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1183,16 +1183,12 @@ public int CompareTo(BigInteger other)
return _sign < other._sign ? -1 : _sign > other._sign ? +1 : 0;
return -other._sign;
}
int cuThis, cuOther;
if (other._bits == null || (cuThis = _bits.Length) > (cuOther = other._bits.Length))

if (other._bits == null)
return _sign;
if (cuThis < cuOther)
return -_sign;

int cuDiff = GetDiffLength(_bits, other._bits, cuThis);
if (cuDiff == 0)
return 0;
return _bits[cuDiff - 1] < other._bits[cuDiff - 1] ? -_sign : _sign;
int bitsResult = BigIntegerCalculator.Compare(_bits, other._bits);
return _sign < 0 ? -bitsResult : bitsResult;
}

public int CompareTo(object? obj)
Expand Down Expand Up @@ -3117,16 +3113,6 @@ private bool GetPartsForBitManipulation(Span<uint> xd)
return _sign < 0;
}

internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu)
{
for (int iv = cu; --iv >= 0;)
{
if (rgu1[iv] != rgu2[iv])
return iv + 1;
}
return 0;
}

[Conditional("DEBUG")]
private void AssertValid()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Numerics
{
internal static partial class BigIntegerCalculator
Expand All @@ -15,22 +17,18 @@ internal const

public static int Compare(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right)
{
if (left.Length < right.Length)
return -1;
if (left.Length > right.Length)
return 1;
Debug.Assert(left.Length <= right.Length || left.Slice(right.Length).ContainsAnyExcept(0u));
Debug.Assert(left.Length >= right.Length || right.Slice(left.Length).ContainsAnyExcept(0u));

for (int i = left.Length - 1; i >= 0; i--)
{
uint leftElement = left[i];
uint rightElement = right[i];
if (leftElement < rightElement)
return -1;
if (leftElement > rightElement)
return 1;
}
if (left.Length != right.Length)
return left.Length < right.Length ? -1 : 1;

int iv = left.Length;
while (--iv >= 0 && left[iv] == right[iv]) ;

return 0;
if (iv < 0)
return 0;
return left[iv] < right[iv] ? -1 : 1;
}

private static int ActualLength(ReadOnlySpan<uint> value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,40 @@ private static void RunPositiveTests(Random random)

BigInteger b1 = new BigInteger(byteArray);
VerifyComparison(BigInteger.One, false, b1 / b1, false, 0);

// BigIntegers constructed with a byte[]
{
int byteLength = 17;
var byteArray1 = new byte[byteLength];
var byteArray2 = new byte[byteLength];

byteArray1.AsSpan().Fill(1);
byteArray2.AsSpan().Fill(1);

// Equals
byteArray1[0] = 2;
byteArray2[0] = 2;
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, 0);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, 0);
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);

// Smaller
byteArray1[0] = 2;
byteArray2[0] = 3;
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, -1);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, 1);
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);

// Larger
byteArray1[0] = 3;
byteArray2[0] = 2;
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, 1);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, -1);
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);
}
}

private static void RunNegativeTests(Random random)
Expand Down Expand Up @@ -636,7 +670,7 @@ private static void VerifyComparison(BigInteger x, bool IsXNegative, BigInteger
Assert.Equal(expectedGreaterThan || expectedEquals, x >= y);
Assert.Equal(expectedLessThan || expectedEquals, y >= x);
}

private static void VerifyCompareResult(int expected, int actual)
{
if (0 == expected)
Expand Down

0 comments on commit 9bbfa9a

Please sign in to comment.