Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UT Neo.Extensions #3467

Merged
merged 10 commits into from
Aug 30, 2024
5 changes: 5 additions & 0 deletions src/Neo.Extensions/BigIntegerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public static BigInteger Mod(this BigInteger x, BigInteger y)

public static BigInteger ModInverse(this BigInteger a, BigInteger n)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used inside the vm?

Copy link
Member

@AnnaShaleva AnnaShaleva Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't, VM uses its own implementation of ModInverse for OpCode.MODPOW handling.

But this method is used for operations with ECPoint (division and addition), thus I'd check if native CryptoLib's contract operations are not affected.

if (BigInteger.GreatestCommonDivisor(a, n) != 1)
{
throw new ArithmeticException("No modular inverse exists for the given inputs.");
}

BigInteger i = n, v = 0, d = 1;
while (a > 0)
{
Expand Down
149 changes: 147 additions & 2 deletions tests/Neo.Extensions.Tests/UT_BigIntegerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

using FluentAssertions;
using Neo.Extensions;
using Neo.Json;
using System;
using System.Collections.Generic;
using System.Numerics;

namespace Neo.Extensions.Tests
Expand All @@ -32,16 +35,158 @@ public void TestGetLowestSetBit()

var big4 = new BigInteger(long.MinValue);
big4.GetLowestSetBit().Should().Be(63);

var big5 = new BigInteger(-18);
big5.GetLowestSetBit().Should().Be(1);

var big6 = BigInteger.Pow(2, 1000);
big6.GetLowestSetBit().Should().Be(1000);
}

[TestMethod]
public void TestGetLowestSetBit_EdgeCases()
{
BigInteger.MinusOne.GetLowestSetBit().Should().Be(0);
BigInteger.One.GetLowestSetBit().Should().Be(0);
new BigInteger(ulong.MaxValue).GetLowestSetBit().Should().Be(0);
(BigInteger.One << 1000).GetLowestSetBit().Should().Be(1000);
}

[TestMethod]
public void TestToByteArrayStandard()
{
BigInteger number = BigInteger.Zero;
Assert.AreEqual("", number.ToByteArrayStandard().ToHexString());
number.ToByteArrayStandard().Should().BeEmpty();

number = BigInteger.One;
Assert.AreEqual("01", number.ToByteArrayStandard().ToHexString());
number.ToByteArrayStandard().Should().Equal(new byte[] { 0x01 });

number = new BigInteger(256); // Binary: 100000000
number.ToByteArrayStandard().Should().Equal(new byte[] { 0x00, 0x01 });
}

[TestMethod]
public void TestToByteArrayStandard_EdgeCases()
{
BigInteger.MinusOne.ToByteArrayStandard().Should().Equal(new byte[] { 0xFF });
new BigInteger(byte.MaxValue).ToByteArrayStandard().Should().Equal(new byte[] { 0xFF, 0x00 });
new BigInteger(ushort.MaxValue).ToByteArrayStandard().Should().Equal(new byte[] { 0xFF, 0xFF, 0x00 });
new BigInteger(JNumber.MIN_SAFE_INTEGER).ToByteArrayStandard().Should().Equal(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0 });
}

[TestMethod]
public void TestMod()
{
var x = new BigInteger(-13);
var y = new BigInteger(5);
var result = x.Mod(y);
result.Should().Be(2); // -13 % 5 is -3, but Mod method should return 2
}

[TestMethod]
public void TestMod_EdgeCases()
{
// Test case 1: Mod of zero
BigInteger.Zero.Mod(5).Should().Be(0, "Mod of zero should always be zero");

// Test case 2: Mod of -1
BigInteger.MinusOne.Mod(5).Should().Be(4, "Mod of -1 should return the modulus minus 1");

// Test case 3: Mod with large numbers
BigInteger minValue = new BigInteger(long.MinValue);
BigInteger maxValue = new BigInteger(long.MaxValue);
minValue.Mod(maxValue).Should().Be(9223372036854775806, "Mod with large numbers should be calculated correctly");

// Test case 4: Comparing Mod with % operator
BigInteger result = minValue.Mod(maxValue);
result.Should().NotBe(long.MinValue % long.MaxValue, "Mod should always return non-negative values, unlike % operator");

// Test case 5: Verifying % operator behavior
(long.MinValue % long.MaxValue).Should().Be((long)(minValue % maxValue), "% operator should behave consistently for BigInteger and long");

// Test case 6: Mod with prime numbers
new BigInteger(17).Mod(19).Should().Be(17, "Mod with a larger prime should return the original number");

// Test case 7: Mod with powers of 2
new BigInteger(1024).Mod(16).Should().Be(0, "Mod with powers of 2 should utilize bitwise operations efficiently");
}

[TestMethod]
public void TestModInverse()
{
var a = new BigInteger(3);
var n = new BigInteger(11);
var result = a.ModInverse(n);
result.Should().Be(4); // 3 * 4 % 11 == 1

a = new BigInteger(1);
n = new BigInteger(11);
result = a.ModInverse(n);
result.Should().Be(1); // 1 * 1 % 11 == 1

a = new BigInteger(13);
n = new BigInteger(11);
result = a.ModInverse(n);
result.Should().Be(6); // 13 % 11 = 2, and 2 * 6 % 11 == 1

a = new BigInteger(6);
n = new BigInteger(12); // 6 and 12 are not coprime
Action act = () => a.ModInverse(n);
act.Should().Throw<ArithmeticException>()
.WithMessage("No modular inverse exists for the given inputs.");
}

[TestMethod]
public void TestModInverse_EdgeCases()
{
Action act = () => BigInteger.Zero.ModInverse(11);
act.Should().Throw<ArithmeticException>();

BigInteger.One.ModInverse(2).Should().Be(1);

act = () => new BigInteger(2).ModInverse(4);
act.Should().Throw<ArithmeticException>();

new BigInteger(long.MaxValue - 1).ModInverse(long.MaxValue).Should().Be(long.MaxValue - 1);
}

[TestMethod]
public void TestBit()
{
var bigInteger = new BigInteger(5); // Binary: 101
var result = bigInteger.TestBit(2);
result.Should().BeTrue(); // Bit at index 2 is set (1)

bigInteger = new BigInteger(5); // Binary: 101
result = bigInteger.TestBit(1);
result.Should().BeFalse(); // Bit at index 1 is not set (0)
}

[TestMethod]
public void TestBit_EdgeCases()
{
BigInteger.Zero.TestBit(0).Should().BeFalse();
BigInteger.Zero.TestBit(100).Should().BeFalse();
BigInteger.MinusOne.TestBit(0).Should().BeTrue();
BigInteger.MinusOne.TestBit(1000).Should().BeTrue();
(BigInteger.One << 1000).TestBit(1000).Should().BeTrue();
(BigInteger.One << 1000).TestBit(999).Should().BeFalse();
}

[TestMethod]
public void TestSum()
{
var bigIntegers = new List<BigInteger> { 1, 2, 3, 4 };
var result = bigIntegers.Sum();
result.Should().Be(10);
}

[TestMethod]
public void TestSum_EdgeCases()
{
new List<BigInteger>().Sum().Should().Be(0);
new List<BigInteger> { JNumber.MIN_SAFE_INTEGER, JNumber.MAX_SAFE_INTEGER }.Sum().Should().Be(0);
new List<BigInteger> { JNumber.MAX_SAFE_INTEGER, JNumber.MAX_SAFE_INTEGER }.Sum().Should().Be(JNumber.MAX_SAFE_INTEGER * 2);
}
}
}
24 changes: 24 additions & 0 deletions tests/Neo.Extensions.Tests/UT_ByteExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,29 @@ public void TestToHexString()
str1.ToHexString(false).Should().Be("6e656f");
str1.ToHexString(true).Should().Be("6f656e");
}

[TestMethod]
public void TestReadOnlySpanToHexString()
{
byte[] input = { 0x0F, 0xA4, 0x3B };
var span = new ReadOnlySpan<byte>(input);
string result = span.ToHexString();
result.Should().Be("0fa43b");

input = Array.Empty<byte>();
span = new ReadOnlySpan<byte>(input);
result = span.ToHexString();
result.Should().BeEmpty();

input = new byte[] { 0x5A };
span = new ReadOnlySpan<byte>(input);
result = span.ToHexString();
result.Should().Be("5a");

input = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
span = new ReadOnlySpan<byte>(input);
result = span.ToHexString();
result.Should().Be("0123456789abcdef");
}
}
}
Loading