diff --git a/src/Nethermind.Int256.Test/Convertibles.cs b/src/Nethermind.Int256.Test/Convertibles.cs index 18ee10c..7a9ec43 100644 --- a/src/Nethermind.Int256.Test/Convertibles.cs +++ b/src/Nethermind.Int256.Test/Convertibles.cs @@ -80,6 +80,9 @@ public static (Type type, BigInteger? min, BigInteger? max)[] ConvertibleTypes = public static IEnumerable TestCases => GenerateTestCases(Numbers, BigInteger.Zero); public static IEnumerable SignedTestCases => GenerateTestCases(SignedNumbers); + + public static IEnumerable TestCasesConvertFrom => GenerateTestCasesConvertFrom(Numbers); + private static IEnumerable GenerateTestCases(IEnumerable<(object, string)> numbers, BigInteger? minValue = null) { Type ExpectedException(BigInteger value, BigInteger? min, BigInteger? max) => @@ -111,4 +114,31 @@ string ExpectedString(Type type, BigInteger value, BigInteger? min, ref Type exp } } } + + private static IEnumerable GenerateTestCasesConvertFrom(IEnumerable<(object, string)> numbers) + { + BigInteger Convert(Type type, BigInteger number) => + type switch + { + var t when type == typeof(float) => (BigInteger)(float)number, + var t when type == typeof(double) => (BigInteger)(double)number, + _ => number + }; + + foreach ((object number, string name) in numbers) + { + foreach ((Type type, BigInteger? min, BigInteger? max) in ConvertibleTypes) + { + BigInteger value = BigInteger.Parse(number.ToString()!); + if (type != typeof(BigInteger) && (value <= min || value >= max)) + continue; + + // we need this because of rounding errors + value = Convert(type, value); + + string testName = $"Convert({name}, {type.Name})"; + yield return new TestCaseData(type, value) { TestName = testName }; + } + } + } } diff --git a/src/Nethermind.Int256.Test/UInt256Tests.cs b/src/Nethermind.Int256.Test/UInt256Tests.cs index 773abce..691967b 100644 --- a/src/Nethermind.Int256.Test/UInt256Tests.cs +++ b/src/Nethermind.Int256.Test/UInt256Tests.cs @@ -33,6 +33,11 @@ public virtual void Add((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + a.Add(b, out a); + a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -58,6 +63,19 @@ public virtual void AddOverflow((BigInteger A, BigInteger B) test) { overflow.Should().Be(true); } + + overflow = T.AddOverflow(uint256a, uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); + if (test.A + test.B <= (BigInteger)UInt256.MaxValue) + { + overflow.Should().Be(false); + } + else + { + overflow.Should().Be(true); + } } [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] @@ -78,6 +96,11 @@ public virtual void AddMod((BigInteger A, BigInteger B, BigInteger M) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.AddMod(uint256b, uint256m, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -98,6 +121,11 @@ public virtual void Subtract((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.Subtract(uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] @@ -130,6 +158,11 @@ protected void SubtractModCore((BigInteger A, BigInteger B, BigInteger M) test, res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.SubtractMod(uint256b, uint256m, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -148,13 +181,18 @@ public virtual void SubtractOverflow((BigInteger A, BigInteger B) test) { UInt256 res = a - b; res.Convert(out resUInt256); + resUInt256.Should().Be(resBigInt); } else { uint256a.Subtract(uint256b, out T res); res.Convert(out resUInt256); + resUInt256.Should().Be(resBigInt); + + uint256a.Subtract(uint256b, out uint256a); + uint256a.Convert(out resUInt256); + resUInt256.Should().Be(resBigInt); } - resUInt256.Should().Be(resBigInt); } else { @@ -169,6 +207,10 @@ public virtual void SubtractOverflow((BigInteger A, BigInteger B) test) uint256a.Subtract(uint256b, out T res); res.Convert(out resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.Subtract(uint256b, out uint256a); + uint256a.Convert(out resUInt256); + resUInt256.Should().Be(resBigInt); } } } @@ -185,6 +227,11 @@ public virtual void Multiply((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.Multiply(uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] @@ -204,6 +251,11 @@ public virtual void MultiplyMod((BigInteger A, BigInteger B, BigInteger M) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.MultiplyMod(uint256b, uint256m, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -222,6 +274,11 @@ public virtual void Div((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.Divide(in uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -236,6 +293,11 @@ public virtual void And((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + T.And(uint256a, uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -250,6 +312,11 @@ public virtual void Or((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + T.Or(uint256a, uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] @@ -264,6 +331,11 @@ public virtual void Xor((BigInteger A, BigInteger B) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + T.Xor(uint256a, uint256b, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] @@ -279,6 +351,11 @@ public virtual void Exp((BigInteger A, int n) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.Exp(convertFromInt(test.n), out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] @@ -300,6 +377,11 @@ public virtual void ExpMod((BigInteger A, BigInteger B, BigInteger M) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.ExpMod(uint256b, uint256m, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] @@ -318,6 +400,11 @@ public virtual void Lsh((BigInteger A, int n) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.LeftShift(test.n, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] @@ -336,6 +423,11 @@ public virtual void Rsh((BigInteger A, int n) test) res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + uint256a.RightShift(test.n, out uint256a); + uint256a.Convert(out resUInt256); + + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.TestCases))] @@ -359,6 +451,10 @@ public virtual void Not(BigInteger test) T.Not(uint256, out T res); res.Convert(out BigInteger resUInt256); resUInt256.Should().Be(resBigInt); + + T.Not(in uint256, out uint256); + uint256.Convert(out resUInt256); + resUInt256.Should().Be(resBigInt); } [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.TestCases))] @@ -469,5 +565,32 @@ string Expected(string valueString) catch (Exception e) when (e.GetType() == expectedException) { } } } + + [TestCaseSource(typeof(Convertibles), nameof(Convertibles.TestCasesConvertFrom))] + public void ConvertFrom(Type type, BigInteger bigIntValue) + { + UInt256 ConvertToUInt256(Type type, BigInteger number) => + type switch + { + var t when type == typeof(byte) => (UInt256)(byte)number, + var t when type == typeof(sbyte) => (UInt256)(sbyte)number, + var t when type == typeof(short) => (UInt256)(short)number, + var t when type == typeof(ushort) => (UInt256)(ushort)number, + var t when type == typeof(int) => (UInt256)(int)number, + var t when type == typeof(uint) => (UInt256)(uint)number, + var t when type == typeof(long) => (UInt256)(long)number, + var t when type == typeof(ulong) => (UInt256)(ulong)number, + var t when type == typeof(float) => (UInt256)(float)number, + var t when type == typeof(double) => (UInt256)(double)number, + var t when type == typeof(decimal) => (UInt256)(decimal)number, + var t when type == typeof(BigInteger) => (UInt256)(BigInteger)number, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + + UInt256 res = ConvertToUInt256(type, bigIntValue); + res.Convert(out BigInteger resUInt256); + + resUInt256.Should().BeEquivalentTo(bigIntValue); + } } } diff --git a/src/Nethermind.Int256/Int256.cs b/src/Nethermind.Int256/Int256.cs index 60d0aa0..db0dc74 100644 --- a/src/Nethermind.Int256/Int256.cs +++ b/src/Nethermind.Int256/Int256.cs @@ -177,9 +177,9 @@ public static void Multiply(in Int256 a, in Int256 b, out Int256 res) b.Neg(out bv); } UInt256.Multiply(av._value, bv._value, out UInt256 ures); - res = new Int256(ures); int aSign = a.Sign; int bSign = b.Sign; + res = new Int256(ures); if ((aSign < 0 && bSign < 0) || (aSign >= 0 && bSign >= 0)) { return; diff --git a/src/Nethermind.Int256/UInt256.cs b/src/Nethermind.Int256/UInt256.cs index bee55ba..1258bbe 100644 --- a/src/Nethermind.Int256/UInt256.cs +++ b/src/Nethermind.Int256/UInt256.cs @@ -221,7 +221,8 @@ public static explicit operator UInt256(double a) a = -a; } - if (a <= ulong.MaxValue) + // don't use (a <= ulong.MaxValue) - this will result in different output on Mac and linux + if (a < ulong.MaxValue) { ulong cu0 = (ulong)a; ulong cu1 = 0; @@ -933,7 +934,7 @@ private static bool SubtractImpl(in UInt256 a, in UInt256 b, out UInt256 res) public void Subtract(in UInt256 b, out UInt256 res) => Subtract(this, b, out res); - public static void SubtractMod(in UInt256 a, in UInt256 b, in UInt256 m, out UInt256 res) + public static void SubtractMod(UInt256 a, in UInt256 b, in UInt256 m, out UInt256 res) { if (SubtractUnderflow(a, b, out res)) { @@ -1024,24 +1025,23 @@ private void Squared(out UInt256 result) result = new UInt256(res); } - public static void Exp(in UInt256 b, in UInt256 e, out UInt256 result) + public static void Exp(UInt256 b, in UInt256 e, out UInt256 result) { result = One; - UInt256 bs = b; int len = e.BitLen; for (int i = 0; i < len; i++) { if (e.Bit(i)) { - Multiply(result, bs, out result); + Multiply(result, b, out result); } - bs.Squared(out bs); + b.Squared(out b); } } public void Exp(in UInt256 exp, out UInt256 res) => Exp(this, exp, out res); - public static void ExpMod(in UInt256 b, in UInt256 e, in UInt256 m, out UInt256 result) + public static void ExpMod(UInt256 b, in UInt256 e, in UInt256 m, out UInt256 result) { if (m.IsOne) { @@ -1049,15 +1049,14 @@ public static void ExpMod(in UInt256 b, in UInt256 e, in UInt256 m, out UInt256 return; } result = One; - UInt256 bs = b; int len = e.BitLen; for (int i = 0; i < len; i++) { if (e.Bit(i)) { - MultiplyMod(result, bs, m, out result); + MultiplyMod(result, b, m, out result); } - MultiplyMod(bs, bs, m, out bs); + MultiplyMod(b, b, m, out b); } } @@ -1193,9 +1192,10 @@ public static void Divide(in UInt256 x, in UInt256 y, out UInt256 res) // At this point, we know // x/y ; x > y > 0 - res = default; // initialize with zeros const int length = 4; - Udivrem(ref Unsafe.As(ref res), ref Unsafe.As(ref Unsafe.AsRef(in x)), length, y, out UInt256 _); + UInt256 quot = default; + Udivrem(ref Unsafe.As(ref quot), ref Unsafe.As(ref Unsafe.AsRef(in x)), length, y, out UInt256 _); + res = quot; } public void Divide(in UInt256 a, out UInt256 res) => Divide(this, a, out res); @@ -1278,8 +1278,7 @@ public static void Lsh(in UInt256 x, int n, out UInt256 res) } } - res = Zero; - ulong z0 = res.u0, z1 = res.u1, z2 = res.u2, z3 = res.u3; + ulong z0 = 0, z1 = 0, z2 = 0, z3 = 0; ulong a = 0, b = 0; // Big swaps first if (n > 192) @@ -1372,8 +1371,7 @@ public static void Rsh(in UInt256 x, int n, out UInt256 res) } } - res = Zero; - ulong z0 = res.u0, z1 = res.u1, z2 = res.u2, z3 = res.u3; + ulong z0 = 0, z1 = 0, z2 = 0, z3 = 0; ulong a = 0, b = 0; // Big swaps first if (n > 192)