Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
132 changes: 130 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,141 @@ 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()
{
BigInteger.Zero.Mod(5).Should().Be(0);
BigInteger.MinusOne.Mod(5).Should().Be(4);
new BigInteger(long.MinValue).Mod(long.MaxValue).Should().Be(9223372036854775806);
// Sign of the result: % can return negative values, while Mod always returns non-negative values.
var longMod = long.MinValue % long.MaxValue;
new BigInteger(long.MinValue).Mod(long.MaxValue).Should().NotBe(longMod);
}

[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");
}
}
}