├── .gitattributes ├── global.json ├── README.md ├── src ├── Int256.slnx ├── Nethermind.Int256.Benchmark │ ├── Program.cs │ ├── Nethermind.Int256.Benchmark.csproj │ ├── Numbers.cs │ ├── NoIntrinsicsJobAttribute.cs │ └── Benchmarks.cs ├── Directory.Packages.props ├── Directory.Build.props ├── Nethermind.Int256.Tests │ ├── Nethermind.Int256.Tests.csproj │ ├── TestNumbers.cs │ ├── TernaryOps.cs │ ├── BinaryOps.cs │ ├── UnaryOps.cs │ ├── Int256Tests.cs │ ├── Convertibles.cs │ └── UInt256Tests.cs └── Nethermind.Int256 │ ├── BigIntegerExtensions.cs │ ├── IInteger.cs │ ├── Nethermind.Int256.csproj │ └── Int256.cs ├── LICENSE ├── NOTICE ├── .github ├── workflows │ └── test-publish.yml └── copilot-instructions.md ├── .editorconfig └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "10.0.100", 4 | "allowPrerelease": false, 5 | "rollForward": "latestMajor" 6 | }, 7 | "test": { 8 | "runner": "Microsoft.Testing.Platform" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Int256 2 | 3 | [![Tests / Publish](https://github.com/nethermindeth/int256/actions/workflows/test-publish.yml/badge.svg)](https://github.com/nethermindeth/int256/actions/workflows/test-publish.yml) 4 | [![Nethermind.Numerics.Int256](https://img.shields.io/nuget/v/Nethermind.Numerics.Int256)](https://www.nuget.org/packages/Nethermind.Numerics.Int256) 5 | 6 | A high-performance implementation of 256-bit integer — signed and unsigned. 7 | -------------------------------------------------------------------------------- /src/Int256.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using BenchmarkDotNet.Configs; 3 | using BenchmarkDotNet.Running; 4 | 5 | namespace Nethermind.Int256.Benchmark 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | IConfig config = Debugger.IsAttached ? new DebugInProcessConfig() : DefaultConfig.Instance; 12 | 13 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Benchmark/Nethermind.Int256.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | true 6 | latest 7 | enable 8 | net10.0 9 | true 10 | 11 | 12 | 13 | Nethermind 14 | Demerzel Solutions Limited 15 | 1.3.6 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/Nethermind.Int256.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | false 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Benchmark/Numbers.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nethermind.Int256.Benchmark 4 | { 5 | public static class Numbers 6 | { 7 | public static readonly BigInteger TwoTo64 = new BigInteger(ulong.MaxValue) + 1; 8 | public static readonly BigInteger TwoTo128 = TwoTo64 * TwoTo64; 9 | public static readonly BigInteger UInt128Max = TwoTo128 - 1; 10 | public static readonly BigInteger TwoTo192 = TwoTo128 * TwoTo64; 11 | public static readonly BigInteger UInt192Max = TwoTo192 - 1; 12 | public static readonly BigInteger TwoTo256 = TwoTo128 * TwoTo128; 13 | public static readonly BigInteger UInt256Max = TwoTo256 - 1; 14 | 15 | public static readonly BigInteger Int256Max = (BigInteger.One << 255) - 1; 16 | public static readonly BigInteger Int256Min = -Int256Max; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/TestNumbers.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Nethermind.Int256.Test 4 | { 5 | public static class TestNumbers 6 | { 7 | public static readonly BigInteger TwoTo64 = new BigInteger(ulong.MaxValue) + 1; 8 | public static readonly BigInteger TwoTo128 = TwoTo64 * TwoTo64; 9 | public static readonly BigInteger UInt128Max = TwoTo128 - 1; 10 | public static readonly BigInteger TwoTo192 = TwoTo128 * TwoTo64; 11 | public static readonly BigInteger UInt192Max = TwoTo192 - 1; 12 | public static readonly BigInteger TwoTo256 = TwoTo128 * TwoTo128; 13 | public static readonly BigInteger UInt256Max = TwoTo256 - 1; 14 | 15 | public static readonly BigInteger Int256Max = (BigInteger.One << 255) - 1; 16 | public static readonly BigInteger Int256Min = -Int256Max; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Demerzel Solutions Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Nethermind.Int256/BigIntegerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | 4 | namespace Nethermind.Int256 5 | { 6 | public static class BigIntegerExtensions 7 | { 8 | public static byte[] ToBytes32(this BigInteger value, bool isBigEndian) 9 | { 10 | byte[] bytes32 = new byte[32]; 11 | value.ToBytes32(bytes32.AsSpan(), isBigEndian); 12 | return bytes32; 13 | } 14 | 15 | public static void ToBytes32(this BigInteger value, Span target, bool isBigEndian) 16 | { 17 | if (!isBigEndian) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | 22 | if (target.Length != 32) 23 | { 24 | throw new ArgumentException($"Target length should be 32 and is {target.Length}", nameof(target)); 25 | } 26 | 27 | ReadOnlySpan bytes = value.ToByteArray(true, true); 28 | if (bytes.Length > 32) 29 | { 30 | bytes.Slice(bytes.Length - 32, bytes.Length).CopyTo(target); 31 | } 32 | else 33 | { 34 | bytes.CopyTo(target.Slice(32 - bytes.Length, bytes.Length)); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Nethermind.Int256/IInteger.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Numerics; 3 | 4 | namespace Nethermind.Int256 5 | { 6 | public interface IInteger where T : IInteger 7 | { 8 | void Add(in T a, out T res); 9 | void AddMod(in T a, in T m, out T res); 10 | 11 | void Subtract(in T a, out T res); 12 | void SubtractMod(in T a, in T m, out T res); 13 | 14 | void Multiply(in T a, out T res); 15 | void MultiplyMod(in T a, in T m, out T res); 16 | 17 | void Divide(in T a, out T res); 18 | 19 | void Exp(in T e, out T res); 20 | void ExpMod(in T e, in T m, out T res); 21 | 22 | void LeftShift(int n, out T res); 23 | void RightShift(int n, out T res); 24 | 25 | abstract static bool AddOverflow(in T a, in T b, out T res); 26 | abstract static void And(in T a, in T b, out T res); 27 | abstract static void Or(in T a, in T b, out T res); 28 | abstract static void Xor(in T a, in T b, out T res); 29 | abstract static void Not(in T a, out T res); 30 | 31 | void Convert(out BigInteger big); 32 | 33 | string ToString(); 34 | 35 | T OneValue { get; } 36 | 37 | T ZeroValue { get; } 38 | 39 | bool IsZero { get; } 40 | 41 | bool IsOne { get; } 42 | 43 | T MaximalValue { get; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Nethermind.Int256/Nethermind.Int256.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | true 6 | 7 | 8 | 9 | A high-performance implementation of 256-bit integer — signed and unsigned 10 | true 11 | true 12 | Nethermind.Numerics.Int256 13 | MIT 14 | https://nethermind.io 15 | README.md 16 | int256 uint256 17 | git 18 | https://github.com/nethermindeth/int256 19 | snupkg 20 | 21 | 22 | 23 | 24 | all 25 | runtime; build; native; contentfiles; analyzers; buildtransitive 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This software incorporates material from third-party 2 | located at https://github.com/holiman/uint256 3 | and covered by the following copyright and permission notice: 4 | 5 | BSD 3-Clause License 6 | 7 | Copyright 2020 uint256 Authors 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | 3. Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/TernaryOps.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using System.Linq; 4 | 5 | namespace Nethermind.Int256.Test 6 | { 7 | public static class TernaryOps 8 | { 9 | public static IEnumerable<(BigInteger, BigInteger, BigInteger)> TestCases 10 | { 11 | get 12 | { 13 | foreach ((var case1, var case2) in BinaryOps.TestCases) 14 | { 15 | foreach (var case3 in UnaryOps.TestCases) 16 | { 17 | yield return (case1, case2, case3); 18 | } 19 | } 20 | } 21 | } 22 | 23 | public static IEnumerable<(BigInteger, BigInteger, BigInteger)> SignedTestCases 24 | { 25 | get 26 | { 27 | foreach ((var case1, var case2) in BinaryOps.SignedTestCases) 28 | { 29 | foreach (var case3 in UnaryOps.SignedTestCases) 30 | { 31 | yield return (case1, case2, case3); 32 | } 33 | } 34 | } 35 | } 36 | 37 | public static IEnumerable<(BigInteger, BigInteger, BigInteger)> SignedModTestCases => SignedTestCases.Where(v => v.Item3 >= 0); 38 | 39 | public static IEnumerable<(ulong, ulong, ulong)> ULongTestCases 40 | { 41 | get 42 | { 43 | foreach ((var case1, var case2) in BinaryOps.ULongTestCases) 44 | { 45 | foreach (var case3 in UnaryOps.ULongTestCases) 46 | { 47 | yield return (case1, case2, case3); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/workflows/test-publish.yml: -------------------------------------------------------------------------------- 1 | name: Test / Publish 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main] 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | inputs: 11 | publish: 12 | description: Publish to NuGet 13 | default: None 14 | type: choice 15 | options: 16 | - 'No' 17 | - Staging 18 | - Production 19 | 20 | jobs: 21 | test-publish: 22 | name: Test 23 | runs-on: ${{ matrix.os }} 24 | strategy: 25 | matrix: 26 | os: [ubuntu-latest, windows-latest, macos-latest] 27 | build-config: [debug, release] 28 | hardware-intrinsics: [0, 1] 29 | env: 30 | DOTNET_EnableHWIntrinsic: ${{ matrix.hardware-intrinsics }} 31 | steps: 32 | - name: Check out repository 33 | uses: actions/checkout@v5 34 | 35 | - name: Set up .NET 36 | uses: actions/setup-dotnet@v5 37 | 38 | - name: Install dependencies 39 | working-directory: src 40 | run: dotnet restore 41 | 42 | - name: Test 43 | working-directory: src/Nethermind.Int256.Tests 44 | run: dotnet test -c ${{ matrix.build-config }} --no-restore 45 | 46 | - name: Publish 47 | if: matrix.build-config == 'release' && matrix.hardware-intrinsics == 1 && 48 | (github.event_name == 'workflow_dispatch' && github.event.inputs.publish != 'No' || github.event_name == 'release') 49 | working-directory: src 50 | run: | 51 | dotnet pack ./Nethermind.Int256 -c ${{ matrix.build-config }} --no-build 52 | dotnet nuget push ./artifacts/package/${{ matrix.build-config }}/*.nupkg \ 53 | -k ${{ github.event.inputs.publish == 'Staging' && secrets.NUGETTEST_API_KEY || secrets.NUGET_API_KEY }} \ 54 | -s ${{ github.event.inputs.publish == 'Staging' && 'https://apiint.nugettest.org/v3/index.json' || 'https://api.nuget.org/v3/index.json' }} 55 | 56 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/BinaryOps.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | 4 | namespace Nethermind.Int256.Test 5 | { 6 | public static class BinaryOps 7 | { 8 | public static IEnumerable<(BigInteger, BigInteger)> TestCases 9 | { 10 | get 11 | { 12 | foreach (var case1 in UnaryOps.TestCases) 13 | { 14 | foreach (var case2 in UnaryOps.TestCases) 15 | { 16 | yield return (case1, case2); 17 | } 18 | } 19 | } 20 | } 21 | 22 | public static IEnumerable<(BigInteger, BigInteger)> SignedTestCases 23 | { 24 | get 25 | { 26 | foreach (var case1 in UnaryOps.SignedTestCases) 27 | { 28 | foreach (var case2 in UnaryOps.SignedTestCases) 29 | { 30 | yield return (case1, case2); 31 | } 32 | } 33 | } 34 | } 35 | 36 | public static IEnumerable<(ulong, ulong)> ULongTestCases 37 | { 38 | get 39 | { 40 | foreach (var case1 in UnaryOps.ULongTestCases) 41 | { 42 | foreach (var case2 in UnaryOps.ULongTestCases) 43 | { 44 | yield return (case1, case2); 45 | } 46 | } 47 | } 48 | } 49 | 50 | public static IEnumerable<(BigInteger, int)> ShiftTestCases 51 | { 52 | get 53 | { 54 | foreach (var n in UnaryOps.TestCases) 55 | { 56 | foreach (var s in UnaryOps.ShiftTestCases) 57 | { 58 | yield return (n, s); 59 | } 60 | } 61 | } 62 | } 63 | 64 | public static IEnumerable<(BigInteger, int)> SignedShiftTestCases 65 | { 66 | get 67 | { 68 | foreach (var n in UnaryOps.SignedTestCases) 69 | { 70 | foreach (var s in UnaryOps.ShiftTestCases) 71 | { 72 | yield return (n, s); 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/UnaryOps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Numerics; 5 | 6 | namespace Nethermind.Int256.Test 7 | { 8 | public static class UnaryOps 9 | { 10 | public static IEnumerable TestCases = new[]{ 11 | 0, 12 | 1, 13 | 2, 14 | 3, 15 | short.MaxValue, 16 | ushort.MaxValue - 1, 17 | ushort.MaxValue, 18 | ushort.MaxValue + 1, 19 | int.MaxValue, 20 | uint.MaxValue - 1, 21 | uint.MaxValue, 22 | uint.MaxValue + 1ul, 23 | long.MaxValue, 24 | ulong.MaxValue - 1, 25 | ulong.MaxValue, 26 | BigInteger.Parse("080000000000000008000000000000001", System.Globalization.NumberStyles.HexNumber), 27 | TestNumbers.TwoTo64, 28 | TestNumbers.TwoTo128, 29 | TestNumbers.TwoTo192, 30 | TestNumbers.UInt128Max, 31 | TestNumbers.UInt192Max, 32 | TestNumbers.UInt256Max, 33 | }.Concat(RandomUnsigned(5)); 34 | 35 | public static IEnumerable SignedTestCases = new[]{ 36 | 0, 37 | 1, 38 | 2, 39 | 3, 40 | short.MaxValue, 41 | ushort.MaxValue, 42 | int.MaxValue, 43 | uint.MaxValue, 44 | long.MaxValue, 45 | ulong.MaxValue, 46 | TestNumbers.TwoTo64, 47 | TestNumbers.TwoTo128, 48 | TestNumbers.TwoTo192, 49 | TestNumbers.UInt128Max, 50 | TestNumbers.UInt192Max, 51 | TestNumbers.Int256Max, 52 | TestNumbers.Int256Min, 53 | }.Concat(RandomSigned(5)); 54 | 55 | public static IEnumerable ULongTestCases = 56 | new ulong[]{ 57 | 0ul, 58 | 1ul, 59 | 2ul, 60 | 3ul, 61 | ushort.MaxValue, 62 | int.MaxValue, 63 | uint.MaxValue, 64 | long.MaxValue, 65 | ulong.MaxValue, 66 | }; 67 | 68 | public static IEnumerable ShiftTestCases => Enumerable.Range(0, 257); 69 | 70 | const int Seed = 0; 71 | 72 | public static IEnumerable RandomSigned(int count) 73 | { 74 | Random rand = new(Seed); 75 | byte[] data = new byte[256 / 8]; 76 | for (int i = 0; i < count; i++) 77 | { 78 | rand.NextBytes(data); 79 | yield return new BigInteger(data); 80 | } 81 | } 82 | 83 | public static IEnumerable RandomUnsigned(int count) 84 | { 85 | Random rand = new(Seed); 86 | byte[] data = new byte[256 / 8]; 87 | for (int i = 0; i < count; i++) 88 | { 89 | rand.NextBytes(data); 90 | data[^1] &= 0x7F; 91 | yield return new BigInteger(data); 92 | } 93 | } 94 | 95 | public static IEnumerable RandomInt(int count) 96 | { 97 | Random rand = new(Seed); 98 | for (int i = 0; i < count; i++) 99 | { 100 | yield return rand.Next(); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/Int256Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Numerics; 3 | using FluentAssertions; 4 | using NUnit.Framework; 5 | 6 | namespace Nethermind.Int256.Test 7 | { 8 | [Parallelizable(ParallelScope.All)] 9 | public class Int256Tests : UInt256TestsTemplate 10 | { 11 | 12 | private static BigInteger Postprocess(BigInteger big) 13 | { 14 | var bytes = big.ToByteArray(); 15 | return new BigInteger(bytes.AsSpan().Slice(0, Math.Min(256 / 8, bytes.Length))); 16 | } 17 | 18 | public Int256Tests() : base((BigInteger x) => new Int256(x), (int x) => new Int256(x), Postprocess, TestNumbers.Int256Max) { } 19 | 20 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedTestCases))] 21 | public override void Add((BigInteger A, BigInteger B) test) => base.Add(test); 22 | 23 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.SignedModTestCases))] 24 | public override void AddMod((BigInteger A, BigInteger B, BigInteger M) test) => base.AddMod(test); 25 | 26 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedTestCases))] 27 | public override void Subtract((BigInteger A, BigInteger B) test) => base.Subtract(test); 28 | 29 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.SignedModTestCases))] 30 | public override void SubtractMod((BigInteger A, BigInteger B, BigInteger M) test) => base.SubtractModCore(test, false); 31 | 32 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedTestCases))] 33 | public override void Multiply((BigInteger A, BigInteger B) test) => base.Multiply(test); 34 | 35 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.SignedModTestCases))] 36 | public override void MultiplyMod((BigInteger A, BigInteger B, BigInteger M) test) => base.MultiplyMod(test); 37 | 38 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedTestCases))] 39 | public override void Div((BigInteger A, BigInteger B) test) => base.Div(test); 40 | 41 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedShiftTestCases))] 42 | public override void Exp((BigInteger A, int n) test) => base.Exp(test); 43 | 44 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.SignedModTestCases))] 45 | public override void ExpMod((BigInteger A, BigInteger B, BigInteger M) test) => base.ExpMod(test); 46 | 47 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedShiftTestCases))] 48 | public override void Lsh((BigInteger A, int n) test) => base.Lsh(test); 49 | 50 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.SignedShiftTestCases))] 51 | public override void Rsh((BigInteger A, int n) test) => base.Rsh(test); 52 | 53 | [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.SignedTestCases))] 54 | public override void ToBigIntegerAndBack(BigInteger test) => base.ToBigIntegerAndBack(test); 55 | 56 | [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.SignedTestCases))] 57 | public override void ToString(BigInteger test) => base.ToString(test); 58 | 59 | [TestCaseSource(typeof(Convertibles), nameof(Convertibles.SignedTestCases))] 60 | public void Convert(Type type, object value, Type expectedException, string expectedString) 61 | { 62 | string Expected(string valueString) 63 | { 64 | if (valueString.Contains("Infinity")) 65 | { 66 | return valueString.StartsWith('-') ? "-∞" : "∞"; 67 | } 68 | string expected = valueString.Replace(",", ""); 69 | return type == typeof(float) ? expected[..Math.Min(6, expected.Length)] : type == typeof(double) ? expected[..Math.Min(14, expected.Length)] : expected; 70 | } 71 | 72 | string valueString = value.ToString()!; 73 | Int256 item = (Int256)BigInteger.Parse(valueString); 74 | try 75 | { 76 | string expected = expectedString ?? Expected(valueString); 77 | string convertedValue = Expected(((IFormattable)System.Convert.ChangeType(item, type)).ToString("0.#", null)); 78 | convertedValue.Should().BeEquivalentTo(expected); 79 | } 80 | catch (Exception e) when (e.GetType() == expectedException) { } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | file_header_template = SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited\nSPDX-License-Identifier: LGPL-3.0-only 13 | 14 | [*.cs] 15 | indent_size = 4 16 | 17 | #### Naming styles #### 18 | 19 | # Naming rules 20 | 21 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 22 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 23 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 24 | 25 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 26 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 27 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 28 | 29 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 30 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 31 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 32 | 33 | # Symbol specifications 34 | 35 | dotnet_naming_symbols.interface.applicable_kinds = interface 36 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 37 | dotnet_naming_symbols.interface.required_modifiers = 38 | 39 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 40 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 41 | dotnet_naming_symbols.types.required_modifiers = 42 | 43 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 44 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 45 | dotnet_naming_symbols.non_field_members.required_modifiers = 46 | 47 | # Naming styles 48 | 49 | dotnet_naming_style.begins_with_i.required_prefix = I 50 | dotnet_naming_style.begins_with_i.required_suffix = 51 | dotnet_naming_style.begins_with_i.word_separator = 52 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 53 | 54 | dotnet_naming_style.pascal_case.required_prefix = 55 | dotnet_naming_style.pascal_case.required_suffix = 56 | dotnet_naming_style.pascal_case.word_separator = 57 | dotnet_naming_style.pascal_case.capitalization = pascal_case 58 | 59 | dotnet_naming_style.pascal_case.required_prefix = 60 | dotnet_naming_style.pascal_case.required_suffix = 61 | dotnet_naming_style.pascal_case.word_separator = 62 | dotnet_naming_style.pascal_case.capitalization = pascal_case 63 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 64 | dotnet_style_coalesce_expression = true:suggestion 65 | dotnet_style_null_propagation = true:suggestion 66 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 67 | dotnet_style_prefer_auto_properties = true:silent 68 | dotnet_style_object_initializer = true:suggestion 69 | dotnet_style_prefer_collection_expression = true:suggestion 70 | dotnet_style_collection_initializer = true:suggestion 71 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion 72 | 73 | csharp_indent_labels = one_less_than_current 74 | csharp_using_directive_placement = outside_namespace:silent 75 | csharp_prefer_simple_using_statement = true:suggestion 76 | csharp_prefer_braces = when_multiline:suggestion 77 | csharp_style_namespace_declarations = file_scoped:suggestion 78 | csharp_style_prefer_method_group_conversion = true:silent 79 | csharp_style_prefer_top_level_statements = true:silent 80 | csharp_style_prefer_primary_constructors = true:suggestion 81 | csharp_style_expression_bodied_methods = false:silent 82 | csharp_style_expression_bodied_constructors = false:silent 83 | csharp_style_expression_bodied_operators = false:silent 84 | csharp_style_expression_bodied_properties = true:silent 85 | csharp_style_expression_bodied_indexers = true:silent 86 | csharp_style_expression_bodied_accessors = true:silent 87 | csharp_style_expression_bodied_lambdas = true:silent 88 | csharp_style_expression_bodied_local_functions = false:silent 89 | csharp_style_var_for_built_in_types = true:silent 90 | csharp_style_var_when_type_is_apparent = true:suggestion 91 | csharp_style_var_elsewhere = false:suggestion 92 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/Convertibles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Numerics; 4 | using NUnit.Framework; 5 | 6 | namespace Nethermind.Int256.Test; 7 | 8 | public class Convertibles 9 | { 10 | private static IEnumerable<(object, string)> Numbers = new (object, string)[] 11 | { 12 | (0, "0"), 13 | (1, "1"), 14 | (2, "2"), 15 | (3, "3"), 16 | (byte.MaxValue, "byte.MaxValue"), 17 | (sbyte.MaxValue, "sbyte.MaxValue"), 18 | (short.MaxValue, "short.MaxValue"), 19 | (ushort.MaxValue, "ushort.MaxValue"), 20 | (int.MaxValue, "int.MaxValue"), 21 | (uint.MaxValue, "uint.MaxValue"), 22 | (long.MaxValue, "long.MaxValue"), 23 | (ulong.MaxValue, "ulong.MaxValue"), 24 | (TestNumbers.TwoTo64, "TwoTo64"), 25 | (TestNumbers.TwoTo128, "TwoTo128"), 26 | (TestNumbers.TwoTo192, "TwoTo192"), 27 | (TestNumbers.UInt128Max, "UInt128Max"), 28 | (TestNumbers.UInt192Max, "UInt192Max"), 29 | (TestNumbers.UInt256Max, "UInt256Max"), 30 | }; 31 | 32 | private static IEnumerable<(object, string)> SignedNumbers = new (object, string)[] 33 | { 34 | (0, "0"), 35 | (1, "1"), 36 | (2, "2"), 37 | (3, "3"), 38 | (byte.MaxValue, "byte.MaxValue"), 39 | (sbyte.MaxValue, "sbyte.MaxValue"), 40 | (sbyte.MinValue, "sbyte.MinValue"), 41 | (short.MaxValue, "short.MaxValue"), 42 | (short.MinValue, "short.MinValue"), 43 | (ushort.MaxValue, "ushort.MaxValue"), 44 | (int.MaxValue, "int.MaxValue"), 45 | (int.MinValue, "int.MinValue"), 46 | (uint.MaxValue, "uint.MaxValue"), 47 | (long.MaxValue, "long.MaxValue"), 48 | (long.MinValue, "long.MinValue"), 49 | (ulong.MaxValue, "ulong.MaxValue"), 50 | (TestNumbers.TwoTo64, "TwoTo64"), 51 | (TestNumbers.TwoTo128, "TwoTo128"), 52 | (TestNumbers.TwoTo192, "TwoTo192"), 53 | (TestNumbers.UInt128Max, "UInt128Max"), 54 | (TestNumbers.UInt192Max, "UInt192Max"), 55 | (-TestNumbers.TwoTo64, "-TwoTo64"), 56 | (-TestNumbers.TwoTo128, "-TwoTo128"), 57 | (-TestNumbers.TwoTo192, "-TwoTo192"), 58 | (-TestNumbers.UInt128Max, "-UInt128Max"), 59 | (-TestNumbers.UInt192Max, "-UInt192Max"), 60 | (TestNumbers.Int256Max, "Int256Max"), 61 | (TestNumbers.Int256Min, "Int256Min"), 62 | }; 63 | 64 | public static (Type type, BigInteger? min, BigInteger? max)[] ConvertibleTypes = 65 | { 66 | (typeof(byte), byte.MinValue, byte.MaxValue), 67 | (typeof(sbyte), sbyte.MinValue, sbyte.MaxValue), 68 | (typeof(short), short.MinValue, short.MaxValue), 69 | (typeof(ushort), ushort.MinValue, ushort.MaxValue), 70 | (typeof(int), int.MinValue, int.MaxValue), 71 | (typeof(uint), uint.MinValue, uint.MaxValue), 72 | (typeof(long), long.MinValue, long.MaxValue), 73 | (typeof(ulong), ulong.MinValue, ulong.MaxValue), 74 | (typeof(float), (BigInteger?)float.MinValue, (BigInteger?)float.MaxValue), 75 | (typeof(double), (BigInteger?)double.MinValue, (BigInteger?)double.MaxValue), 76 | (typeof(decimal), (BigInteger?)decimal.MinValue, (BigInteger?)decimal.MaxValue), 77 | (typeof(BigInteger), null, null) 78 | }; 79 | 80 | public static IEnumerable TestCases => GenerateTestCases(Numbers, BigInteger.Zero); 81 | public static IEnumerable SignedTestCases => GenerateTestCases(SignedNumbers); 82 | 83 | private static IEnumerable GenerateTestCases(IEnumerable<(object, string)> numbers, BigInteger? minValue = null) 84 | { 85 | Type ExpectedException(BigInteger value, BigInteger? min, BigInteger? max) => 86 | (!min.HasValue || !max.HasValue || (value >= min && value <= max)) && (!minValue.HasValue || value >= minValue) 87 | ? null 88 | : typeof(OverflowException); 89 | 90 | string ExpectedString(Type type, BigInteger value, BigInteger? min, ref Type expectedException) 91 | { 92 | string expectedString = null; 93 | if (expectedException is not null && type == typeof(float)) 94 | { 95 | expectedString = value < min ? "-∞" : "∞"; 96 | expectedException = null; 97 | } 98 | 99 | return expectedString; 100 | } 101 | 102 | foreach ((object number, string name) in numbers) 103 | { 104 | foreach ((Type type, BigInteger? min, BigInteger? max) in ConvertibleTypes) 105 | { 106 | BigInteger value = BigInteger.Parse(number.ToString()!); 107 | Type expectedException = ExpectedException(value, min, max); 108 | string expectedString = ExpectedString(type, value, min, ref expectedException); 109 | string testName = $"Convert({name}, {type.Name}{(expectedException is not null || expectedString?.Contains('∞') == true ? ", over/under flow" : "")})"; 110 | yield return new TestCaseData(type, number, expectedException, expectedString) { TestName = testName }; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Benchmark/NoIntrinsicsJobAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using BenchmarkDotNet.Attributes; 4 | using BenchmarkDotNet.Jobs; 5 | using BenchmarkDotNet.Engines; 6 | using BenchmarkDotNet.Environments; 7 | 8 | namespace Nethermind.Int256.Benchmark 9 | { 10 | public class NoIntrinsicsJobAttribute : JobConfigBaseAttribute 11 | { 12 | public NoIntrinsicsJobAttribute(RuntimeMoniker runtimeMoniker, int launchCount = -1, int warmupCount = -1, int iterationCount = -1, int invocationCount = -1, string id = null, bool baseline = false) 13 | : base(CreateJob(id, launchCount, warmupCount, iterationCount, invocationCount, null, baseline, runtimeMoniker) 14 | .WithEnvironmentVariable("DOTNET_EnableHWIntrinsic", "0")) 15 | { 16 | 17 | } 18 | 19 | private static Job CreateJob(string id, int launchCount, int warmupCount, int iterationCount, int invocationCount, RunStrategy? runStrategy, bool baseline, RuntimeMoniker runtimeMoniker = RuntimeMoniker.HostProcess) 20 | { 21 | Job job = new Job(id); 22 | int num = 0; 23 | if (launchCount != -1) 24 | { 25 | job.Run.LaunchCount = launchCount; 26 | num++; 27 | } 28 | 29 | if (warmupCount != -1) 30 | { 31 | job.Run.WarmupCount = warmupCount; 32 | num++; 33 | } 34 | 35 | if (iterationCount != -1) 36 | { 37 | job.Run.IterationCount = iterationCount; 38 | num++; 39 | } 40 | 41 | if (invocationCount != -1) 42 | { 43 | job.Run.InvocationCount = invocationCount; 44 | num++; 45 | int num2 = job.Run.ResolveValue(RunMode.UnrollFactorCharacteristic, EnvironmentResolver.Instance); 46 | if (invocationCount % num2 != 0) 47 | { 48 | job.Run.UnrollFactor = 1; 49 | num++; 50 | } 51 | } 52 | 53 | if (runStrategy.HasValue) 54 | { 55 | job.Run.RunStrategy = runStrategy.Value; 56 | num++; 57 | } 58 | 59 | if (baseline) 60 | { 61 | job.Meta.Baseline = true; 62 | } 63 | 64 | if (runtimeMoniker != 0) 65 | { 66 | job.Environment.Runtime = runtimeMoniker.GetRuntime(); 67 | num++; 68 | } 69 | 70 | if (id == null && num == 1 && runtimeMoniker != 0) 71 | { 72 | job = job.WithId(runtimeMoniker.GetRuntime().Name); 73 | } 74 | 75 | return job.Freeze(); 76 | } 77 | } 78 | internal static class RuntimeMonikerExtensions 79 | { 80 | internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) 81 | { 82 | switch (runtimeMoniker) 83 | { 84 | case RuntimeMoniker.Net461: 85 | return ClrRuntime.Net461; 86 | case RuntimeMoniker.Net462: 87 | return ClrRuntime.Net462; 88 | case RuntimeMoniker.Net47: 89 | return ClrRuntime.Net47; 90 | case RuntimeMoniker.Net471: 91 | return ClrRuntime.Net471; 92 | case RuntimeMoniker.Net472: 93 | return ClrRuntime.Net472; 94 | case RuntimeMoniker.Net48: 95 | return ClrRuntime.Net48; 96 | case RuntimeMoniker.Net481: 97 | return ClrRuntime.Net481; 98 | case RuntimeMoniker.NetCoreApp20: 99 | return CoreRuntime.Core20; 100 | case RuntimeMoniker.NetCoreApp21: 101 | return CoreRuntime.Core21; 102 | case RuntimeMoniker.NetCoreApp22: 103 | return CoreRuntime.Core22; 104 | case RuntimeMoniker.NetCoreApp30: 105 | return CoreRuntime.Core30; 106 | case RuntimeMoniker.NetCoreApp31: 107 | return CoreRuntime.Core31; 108 | case RuntimeMoniker.Net50: 109 | #pragma warning disable CS0618 // Type or member is obsolete 110 | case RuntimeMoniker.NetCoreApp50: 111 | #pragma warning restore CS0618 // Type or member is obsolete 112 | return CoreRuntime.Core50; 113 | case RuntimeMoniker.Net60: 114 | return CoreRuntime.Core60; 115 | case RuntimeMoniker.Net70: 116 | return CoreRuntime.Core70; 117 | case RuntimeMoniker.Net80: 118 | return CoreRuntime.Core80; 119 | case RuntimeMoniker.Net90: 120 | return CoreRuntime.Core90; 121 | case RuntimeMoniker.Net10_0: 122 | return CoreRuntime.Core10_0; 123 | case RuntimeMoniker.Mono: 124 | return MonoRuntime.Default; 125 | case RuntimeMoniker.NativeAot60: 126 | return NativeAotRuntime.Net60; 127 | case RuntimeMoniker.NativeAot70: 128 | return NativeAotRuntime.Net70; 129 | case RuntimeMoniker.NativeAot80: 130 | return NativeAotRuntime.Net80; 131 | case RuntimeMoniker.Mono60: 132 | return MonoRuntime.Mono60; 133 | case RuntimeMoniker.Mono70: 134 | return MonoRuntime.Mono70; 135 | default: 136 | throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported"); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | #!.vscode/settings.json 382 | #!.vscode/tasks.json 383 | #!.vscode/launch.json 384 | #!.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | .idea/ 400 | 401 | ## macOS 402 | .DS_Store 403 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # GitHub Copilot Instructions for Int256 Repository 2 | 3 | ## Project Overview 4 | 5 | This repository contains **Nethermind.Int256**, a high-performance .NET library implementing 256-bit integer types for blockchain and cryptographic applications. The library provides both signed (`Int256`) and unsigned (`UInt256`) 256-bit integer implementations optimized for performance using hardware intrinsics and vectorization. 6 | 7 | ### Key Features 8 | - **High Performance**: Leverages SIMD instructions, vectorization, and hardware intrinsics 9 | - **Complete API**: Implements all standard arithmetic, bitwise, and comparison operations 10 | - **.NET Integration**: C# with .NET 9.0 target framework, full compatibility with .NET numeric interfaces and conversion patterns 11 | - **Cross-Platform**: Supports multiple architectures with optimized code paths 12 | - **Memory Efficient**: Struct-based design with minimal allocation overhead 13 | 14 | 15 | ## General 16 | 17 | - Make only high confidence suggestions when reviewing code changes. 18 | - Always use the latest version C#, currently C# 13 features. 19 | - Never change global.json unless explicitly asked to. 20 | - Never change package.json or package-lock.json files unless explicitly asked to. 21 | - Never change NuGet.config files unless explicitly asked to. 22 | - Always trim trailing whitespace, and do not have whitespace on otherwise empty lines. 23 | 24 | **Any code you commit SHOULD compile, and new and existing tests related to the change SHOULD pass.** 25 | 26 | You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. 27 | 28 | You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). 29 | 30 | In addition to the rules enforced by `.editorconfig`, you SHOULD: 31 | 32 | - Prefer file-scoped namespace declarations and single-line using directives; however do not change the type of namespace format in an existing file unless specifically asked. 33 | - Ensure that the final return statement of a method is on its own line. 34 | - Use pattern matching and switch expressions wherever possible. 35 | - Use `nameof` instead of string literals when referring to member names. 36 | - Always use `is null` or `is not null` instead of `== null` or `!= null`. 37 | - Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. 38 | - Prefer `?.` if applicable (e.g. `scope?.Dispose()`). 39 | - Use `ObjectDisposedException.ThrowIf` where applicable. 40 | - When adding new unit tests, strongly prefer to add them to existing test code files rather than creating new code files. 41 | - If you add new code files, ensure they are listed in the csproj file (if other files in that folder are listed there) so they build. 42 | - When running tests, if possible use filters and check test run counts, or look at test logs, to ensure they actually ran. 43 | - Do not finish work with any tests commented out or disabled that were not previously commented out or disabled. 44 | - When writing tests, do not emit "Act", "Arrange" or "Assert" comments. 45 | - Copy existing style in nearby files for test method names and capitalization. 46 | - Provide code comments when helpful to explain why something is being done; however do not comment what is obvious and just a repeation of the code line. 47 | - Ensure that XML doc comments are created for any public APIs. 48 | - Do NOT use #regions. 49 | - Prefer low allocation and higher performance code. 50 | 51 | --- 52 | 53 | 54 | ## Architecture and Core Components 55 | 56 | ### Core Types 57 | - **`UInt256`**: 256-bit unsigned integer (primary implementation) 58 | - **`Int256`**: 256-bit signed integer (wrapper around UInt256) 59 | - **`IInteger`**: Common interface for integer operations 60 | - **`BigIntegerExtensions`**: Extensions for System.Numerics.BigInteger integration 61 | 62 | ### Internal Structure 63 | ```csharp 64 | // UInt256 uses explicit layout with 4 ulong components 65 | [StructLayout(LayoutKind.Explicit)] 66 | public readonly struct UInt256 67 | { 68 | [FieldOffset(0)] public readonly ulong u0; // Least significant 69 | [FieldOffset(8)] public readonly ulong u1; 70 | [FieldOffset(16)] public readonly ulong u2; 71 | [FieldOffset(24)] public readonly ulong u3; // Most significant 72 | } 73 | ``` 74 | 75 | ### Performance Optimizations 76 | - **Hardware Intrinsics**: Uses `Vector256` when available 77 | - **Conditional Compilation**: Hardware intrinsics can be disabled via `DOTNET_EnableHWIntrinsic=0` 78 | - **Unsafe Operations**: Leverages unsafe code for optimal performance 79 | - **Branch Optimization**: Minimizes conditional branches in hot paths 80 | 81 | ## Development Environment 82 | 83 | ### Requirements 84 | - **.NET 9.0 SDK** (specified in global.json) 85 | - **Visual Studio 2022** or **VS Code** with C# extension 86 | - **Git** for version control 87 | 88 | ### Project Structure 89 | ``` 90 | src/ 91 | ├── Nethermind.Int256/ # Core library 92 | │ ├── Int256.cs # Signed 256-bit integer 93 | │ ├── UInt256.cs # Unsigned 256-bit integer (main implementation) 94 | │ ├── IInteger.cs # Common interface 95 | │ └── BigIntegerExtensions.cs # BigInteger helpers 96 | ├── Nethermind.Int256.Tests/ # Unit tests 97 | └── Nethermind.Int256.Benchmark/ # Performance benchmarks 98 | ``` 99 | 100 | ### Build Commands 101 | ```bash 102 | # Restore dependencies 103 | dotnet restore 104 | 105 | # Build (from src directory) 106 | dotnet build 107 | 108 | # Run tests 109 | dotnet run -c Debug --project src/Nethermind.Int256.Tests 110 | 111 | # Run benchmarks 112 | dotnet run -c Release --project src/Nethermind.Int256.Benchmark 113 | ``` 114 | 115 | ## Coding Standards and Conventions 116 | 117 | ### Code Style 118 | - **Latest C# Language Version**: Uses `latest` 119 | - **Nullable Reference Types**: Enabled throughout the project 120 | - **Unsafe Blocks**: Allowed for performance-critical sections 121 | - **Warnings as Errors**: `true` 122 | - **Formatting**: 4-space indentation for C# files, LF line endings, UTF-8 encoding 123 | - **Interface Naming**: Interfaces should begin with 'I' (e.g., `IInteger`) 124 | - **Type Naming**: PascalCase for types, methods, and properties 125 | 126 | ### Naming Conventions 127 | - **Internal Fields**: Use `u0, u1, u2, u3` for UInt256 components (little-endian order) 128 | - **Constants**: Use PascalCase (e.g., `MaxValue`, `MinValue`) 129 | - **Static Readonly**: Use for compile-time constants 130 | - **Private Static**: Use `s_` prefix for static fields (e.g., `s_instanceRandom`) 131 | 132 | ### Performance Guidelines 133 | 1. **Prefer Struct Operations**: Avoid boxing/unboxing 134 | 2. **Use ReadOnlySpan**: For byte array operations 135 | 3. **Leverage Vector Operations**: When `Vector256.IsSupported` 136 | 4. **Minimize Allocations**: Use `Unsafe.SkipInit()` when appropriate 137 | 5. **Branch Optimization**: Structure code to minimize conditional branches 138 | 139 | ### Memory Layout Considerations 140 | ```csharp 141 | // Always consider endianness 142 | public UInt256(ReadOnlySpan bytes, bool isBigEndian) 143 | 144 | // Use explicit field offsets for predictable layout 145 | [FieldOffset(0)] public readonly ulong u0; // Little-endian: LSB first 146 | ``` 147 | 148 | ## Testing Guidelines 149 | 150 | ### Test Organization 151 | - **Unit Tests**: Located in `Nethermind.Int256.Tests` 152 | - **Test Categories**: Organized by operation type (Binary, Unary, Ternary, Convertibles) 153 | - **Hardware Intrinsics Testing**: Tests run with both enabled and disabled intrinsics 154 | - **Template Pattern**: Uses `UInt256TestsTemplate` for shared test logic 155 | - **FluentAssertions**: Uses FluentAssertions for readable test assertions 156 | - **NUnit Framework**: All tests use NUnit attributes and framework 157 | 158 | ### Test Naming 159 | ```csharp 160 | [Test] 161 | public void OperationName_Scenario_ExpectedResult() 162 | { 163 | // Arrange, Act, Assert pattern 164 | // Use FluentAssertions for readable assertions 165 | result.Should().Be(expected); 166 | } 167 | 168 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 169 | public void Add((BigInteger A, BigInteger B) test) 170 | { 171 | // Use test case sources for comprehensive testing 172 | } 173 | ``` 174 | 175 | ### Test Data 176 | - **TestNumbers.cs**: Contains predefined test values and edge cases 177 | - **Comprehensive Coverage**: Tests include overflow, underflow, and boundary conditions 178 | - **Cross-Platform**: Tests verify behavior across different hardware configurations 179 | 180 | ### Writing New Tests 181 | When adding operations, ensure tests cover: 182 | 1. **Basic Operations**: Normal cases with typical values 183 | 2. **Edge Cases**: Zero, maximum/minimum values, overflow conditions 184 | 3. **Conversion Tests**: To/from other numeric types 185 | 4. **Performance Tests**: For critical path operations 186 | 187 | ## Performance Considerations 188 | 189 | ### Optimization Priorities 190 | 1. **Hot Path Operations**: Arithmetic operations (+, -, *, /, %) 191 | 2. **Comparison Operations**: Equality, less than, greater than 192 | 3. **Bitwise Operations**: AND, OR, XOR, shifts 193 | 4. **Conversion Operations**: To/from BigInteger, primitive types 194 | 195 | ### Hardware Intrinsics Usage 196 | ```csharp 197 | if (Vector256.IsSupported) 198 | { 199 | // Use vectorized operations 200 | Unsafe.As>(ref this.u0) = Vector256.Create(...); 201 | } 202 | else 203 | { 204 | // Fallback to scalar operations 205 | } 206 | ``` 207 | 208 | ### Memory Access Patterns 209 | - **Sequential Access**: Prefer when possible for cache efficiency 210 | - **Alignment**: Struct layout ensures proper alignment 211 | - **Avoid Indirection**: Direct field access over property chains 212 | 213 | ## Build and CI Process 214 | 215 | ### GitHub Actions Workflow 216 | - **Matrix Testing**: Tests against Debug/Release builds 217 | - **Hardware Intrinsics**: Tests with `DOTNET_EnableHWIntrinsic=0` and `=1` 218 | - **Cross-Platform**: Linux-based testing environment 219 | - **NuGet Publishing**: Automated on releases 220 | 221 | ### Local Development 222 | ```bash 223 | # Run full test matrix locally 224 | DOTNET_EnableHWIntrinsic=0 dotnet run -c Debug --project src/Nethermind.Int256.Tests 225 | DOTNET_EnableHWIntrinsic=1 dotnet run -c Release --project src/Nethermind.Int256.Tests 226 | 227 | # Run performance benchmarks 228 | dotnet run -c Release --project src/Nethermind.Int256.Benchmark 229 | ``` 230 | 231 | ### Benchmarking 232 | - **BenchmarkDotNet**: Uses BenchmarkDotNet for performance testing 233 | - **Hardware Variants**: Benchmarks test both with and without hardware intrinsics 234 | - **Baseline Comparisons**: Compare against System.Numerics.BigInteger where applicable 235 | 236 | ### Package Configuration 237 | - **Package ID**: `Nethermind.Numerics.Int256` 238 | - **Target Framework**: net9.0 239 | - **Dependencies**: Minimal (Microsoft.SourceLink.GitHub for debugging) 240 | 241 | ## Common Patterns and Examples 242 | 243 | ### Creating UInt256 Values 244 | ```csharp 245 | // From primitive types 246 | UInt256 value1 = 42ul; 247 | UInt256 value2 = new UInt256(0x12345678, 0x9ABCDEF0, 0, 0); 248 | 249 | // From byte array 250 | var bytes = new byte[32]; 251 | UInt256 value3 = new UInt256(bytes, isBigEndian: true); 252 | 253 | // From BigInteger 254 | BigInteger big = BigInteger.Parse("123456789012345678901234567890"); 255 | UInt256 value4 = (UInt256)big; 256 | ``` 257 | 258 | ### Arithmetic Operations 259 | ```csharp 260 | UInt256 a = 100; 261 | UInt256 b = 200; 262 | UInt256 sum = a + b; 263 | UInt256 product = a * b; 264 | bool isEqual = a == b; 265 | ``` 266 | 267 | ### Performance-Critical Code Patterns 268 | ```csharp 269 | // Use in/out parameters to avoid copies 270 | public static void Add(in UInt256 left, in UInt256 right, out UInt256 result) 271 | 272 | // Leverage hardware intrinsics when available 273 | if (Vector256.IsSupported) 274 | { 275 | // Vectorized implementation 276 | } 277 | else 278 | { 279 | // Scalar fallback 280 | } 281 | 282 | // Follow the IInteger interface pattern for consistency 283 | public void Add(in T a, out T res) 284 | { 285 | // Implementation 286 | } 287 | ``` 288 | 289 | ## Contributing Guidelines 290 | 291 | ### License and Copyright 292 | - **License**: MIT License (see LICENSE file) 293 | - **Copyright**: Demerzel Solutions Limited 294 | 295 | ### Before Making Changes 296 | 1. **Understand Performance Impact**: Profile changes affecting hot paths 297 | 2. **Test Hardware Variants**: Ensure compatibility with/without intrinsics 298 | 3. **Maintain API Compatibility**: Preserve existing public interfaces 299 | 4. **Follow Existing Patterns**: Consistency with established code style 300 | 301 | ### Code Review Focus Areas 302 | - **Performance**: No regression in benchmark results 303 | - **Correctness**: Comprehensive test coverage 304 | - **Security**: Proper handling of edge cases and overflow 305 | - **Maintainability**: Clear, readable implementation 306 | 307 | --- 308 | 309 | This library is a critical component for blockchain and cryptographic applications where performance and correctness are paramount. Always prioritize these concerns when making contributions. 310 | 311 | ## Quick Reference 312 | 313 | ### Key Files to Understand 314 | - `UInt256.cs` - Primary implementation with all core operations 315 | - `Int256.cs` - Signed wrapper around UInt256 316 | - `IInteger.cs` - Common interface defining operation patterns 317 | - `TestNumbers.cs` - Constants and edge cases for testing 318 | 319 | ### Common Operations Pattern 320 | ```csharp 321 | // All operations follow this pattern for consistency and performance 322 | public void OperationName(in UInt256 other, out UInt256 result) 323 | { 324 | // Implementation using 'in' parameters to avoid copies 325 | // and 'out' parameters for results 326 | } 327 | ``` -------------------------------------------------------------------------------- /src/Nethermind.Int256.Benchmark/Benchmarks.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Numerics; 3 | using System.Linq; 4 | using BenchmarkDotNet.Attributes; 5 | using BenchmarkDotNet.Jobs; 6 | using Nethermind.Int256.Test; 7 | 8 | namespace Nethermind.Int256.Benchmark 9 | { 10 | public class UnsingedBenchmarkBase 11 | { 12 | public IEnumerable Values => new[] { Numbers.UInt256Max, BigInteger.One }; 13 | 14 | public IEnumerable ValuesUint256 => Values.Select(x => (UInt256)x); 15 | 16 | public IEnumerable<(BigInteger, UInt256)> ValuesTuple => Values.Select(x => (x, (UInt256)x)); 17 | 18 | public IEnumerable ValuesInt => UnaryOps.RandomInt(3); 19 | 20 | public IEnumerable ValuesIntUint256 => ValuesInt.Select(x => (UInt256)x); 21 | 22 | public IEnumerable<(int, UInt256)> ValuesIntTuple => ValuesInt.Select(x => (x, (UInt256)x)); 23 | } 24 | 25 | public class UnsignedIntTwoParamBenchmarkBase : UnsingedBenchmarkBase 26 | { 27 | [ParamsSource(nameof(ValuesTuple))] 28 | public (BigInteger, UInt256) A; 29 | 30 | [ParamsSource(nameof(ValuesIntTuple))] 31 | public (int, UInt256) D; 32 | } 33 | 34 | public class UnsignedTwoParamBenchmarkBase : UnsingedBenchmarkBase 35 | { 36 | [ParamsSource(nameof(ValuesTuple))] 37 | public (BigInteger, UInt256) A; 38 | 39 | [ParamsSource(nameof(ValuesTuple))] 40 | public (BigInteger, UInt256) B; 41 | } 42 | 43 | public class UnsignedThreeParamBenchmarkBase : UnsignedTwoParamBenchmarkBase 44 | { 45 | [ParamsSource(nameof(ValuesTuple))] 46 | public (BigInteger, UInt256) C; 47 | } 48 | 49 | public class SignedBenchmarkBase 50 | { 51 | public IEnumerable Values => Enumerable.Concat(new[] { Numbers.Int256Max }, UnaryOps.RandomSigned(1)); 52 | 53 | public IEnumerable ValuesPositive => Values.Where(x => x.Sign >= 0); 54 | 55 | public IEnumerable ValuesInt256 => Values.Select(x => (Int256)x); 56 | 57 | public IEnumerable<(BigInteger, Int256)> ValuesTuple => Values.Select(x => (x, (Int256)x)); 58 | 59 | public IEnumerable<(BigInteger, Int256)> ValuesTuplePositive => ValuesPositive.Select(x => (x, (Int256)x)); 60 | 61 | public IEnumerable ValuesInt => UnaryOps.RandomInt(3); 62 | 63 | public IEnumerable ValuesIntInt256 => ValuesInt.Select(x => (Int256)x); 64 | 65 | public IEnumerable<(int, Int256)> ValuesIntTuple => ValuesInt.Select(x => (x, (Int256)x)); 66 | } 67 | 68 | public class SignedTwoParamBenchmarkBase : SignedBenchmarkBase 69 | { 70 | [ParamsSource(nameof(ValuesTuple))] 71 | public (BigInteger, Int256) A; 72 | 73 | [ParamsSource(nameof(ValuesTuple))] 74 | public (BigInteger, Int256) B; 75 | } 76 | 77 | public class SignedThreeParamBenchmarkBase : SignedTwoParamBenchmarkBase 78 | { 79 | [ParamsSource(nameof(ValuesTuple))] 80 | public (BigInteger, Int256) C; 81 | } 82 | 83 | public class SignedIntTwoParamBenchmarkBase : SignedBenchmarkBase 84 | { 85 | [ParamsSource(nameof(ValuesTuple))] 86 | public (BigInteger, Int256) A; 87 | 88 | [ParamsSource(nameof(ValuesIntTuple))] 89 | public (int, Int256) D; 90 | } 91 | 92 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 93 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 94 | [MemoryDiagnoser] 95 | public class LessThanUnsigned : UnsignedTwoParamBenchmarkBase 96 | { 97 | [Benchmark(Baseline = true)] 98 | public bool LessThan_BigInteger() 99 | { 100 | return A.Item1 < B.Item1; 101 | } 102 | 103 | [Benchmark] 104 | public bool LessThan_UInt256() 105 | { 106 | return A.Item2 < B.Item2; 107 | } 108 | } 109 | 110 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 111 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 112 | [MemoryDiagnoser] 113 | public class AddUnsigned : UnsignedTwoParamBenchmarkBase 114 | { 115 | [Benchmark(Baseline = true)] 116 | public BigInteger Add_BigInteger() 117 | { 118 | return (A.Item1 + B.Item1) % Numbers.TwoTo256; 119 | } 120 | 121 | [Benchmark] 122 | public UInt256 Add_UInt256() 123 | { 124 | UInt256.Add(A.Item2, B.Item2, out UInt256 res); 125 | return res; 126 | } 127 | } 128 | 129 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 130 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 131 | [MemoryDiagnoser] 132 | public class AddSigned : SignedTwoParamBenchmarkBase 133 | { 134 | [Benchmark(Baseline = true)] 135 | public BigInteger Add_BigInteger() 136 | { 137 | return (A.Item1 + B.Item1) % Numbers.TwoTo256; 138 | } 139 | 140 | [Benchmark] 141 | public Int256 Add_Int256() 142 | { 143 | Int256.Add(A.Item2, B.Item2, out Int256 res); 144 | return res; 145 | } 146 | } 147 | 148 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 149 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 150 | [MemoryDiagnoser] 151 | public class SubtractUnsigned : UnsignedTwoParamBenchmarkBase 152 | { 153 | [Benchmark(Baseline = true)] 154 | public BigInteger Subtract_BigInteger() 155 | { 156 | return (A.Item1 - B.Item1) % Numbers.TwoTo256; 157 | } 158 | 159 | [Benchmark] 160 | public UInt256 Subtract_UInt256() 161 | { 162 | UInt256.Subtract(A.Item2, B.Item2, out UInt256 res); 163 | return res; 164 | } 165 | } 166 | 167 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 168 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 169 | [MemoryDiagnoser] 170 | public class SubtractSigned : SignedTwoParamBenchmarkBase 171 | { 172 | [Benchmark(Baseline = true)] 173 | public BigInteger Subtract_BigInteger() 174 | { 175 | return (A.Item1 - B.Item1) % Numbers.TwoTo256; 176 | } 177 | 178 | [Benchmark] 179 | public Int256 Subtract_Int256() 180 | { 181 | Int256.Subtract(A.Item2, B.Item2, out Int256 res); 182 | return res; 183 | } 184 | } 185 | 186 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 187 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 188 | [MemoryDiagnoser] 189 | public class AddModUnsinged : UnsignedThreeParamBenchmarkBase 190 | { 191 | [Benchmark(Baseline = true)] 192 | public BigInteger AddMod_BigInteger() 193 | { 194 | return ((A.Item1 + B.Item1) % C.Item1); 195 | } 196 | 197 | [Benchmark] 198 | public UInt256 AddMod_UInt256() 199 | { 200 | UInt256.AddMod(A.Item2, B.Item2, C.Item2, out UInt256 res); 201 | return res; 202 | } 203 | } 204 | 205 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 206 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 207 | [MemoryDiagnoser] 208 | public class AddModSinged : SignedThreeParamBenchmarkBase 209 | { 210 | [Benchmark(Baseline = true)] 211 | public BigInteger AddMod_BigInteger() 212 | { 213 | return ((A.Item1 + B.Item1) % C.Item1); 214 | } 215 | 216 | [Benchmark] 217 | public Int256 AddMod_Int256() 218 | { 219 | Int256.AddMod(A.Item2, B.Item2, C.Item2, out Int256 res); 220 | return res; 221 | } 222 | } 223 | 224 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 225 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 226 | [MemoryDiagnoser] 227 | public class SubtractModUnsinged : UnsignedThreeParamBenchmarkBase 228 | { 229 | [Benchmark(Baseline = true)] 230 | public BigInteger SubtractMod_BigInteger() 231 | { 232 | return ((A.Item1 - B.Item1) % C.Item1); 233 | } 234 | 235 | [Benchmark] 236 | public UInt256 SubtractMod_UInt256() 237 | { 238 | UInt256.SubtractMod(A.Item2, B.Item2, C.Item2, out UInt256 res); 239 | return res; 240 | } 241 | } 242 | 243 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 244 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 245 | [MemoryDiagnoser] 246 | public class SubtractModSigned : SignedThreeParamBenchmarkBase 247 | { 248 | [Benchmark(Baseline = true)] 249 | public BigInteger SubtractMod_BigInteger() 250 | { 251 | return ((A.Item1 - B.Item1) % C.Item1); 252 | } 253 | 254 | [Benchmark] 255 | public Int256 SubtractMod_Int256() 256 | { 257 | Int256.SubtractMod(A.Item2, B.Item2, C.Item2, out Int256 res); 258 | return res; 259 | } 260 | } 261 | 262 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 263 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 264 | [MemoryDiagnoser] 265 | public class MultiplyUnsigned : UnsignedTwoParamBenchmarkBase 266 | { 267 | [Benchmark(Baseline = true)] 268 | public BigInteger Multiply_BigInteger() 269 | { 270 | return (A.Item1 * B.Item1) % Numbers.TwoTo256; 271 | } 272 | 273 | [Benchmark] 274 | public UInt256 Multiply_UInt256() 275 | { 276 | UInt256.Multiply(A.Item2, B.Item2, out UInt256 res); 277 | return res; 278 | } 279 | } 280 | 281 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 282 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 283 | [MemoryDiagnoser] 284 | public class MultiplySigned : SignedTwoParamBenchmarkBase 285 | { 286 | [Benchmark(Baseline = true)] 287 | public BigInteger Multiply_BigInteger() 288 | { 289 | return (A.Item1 * B.Item1) % Numbers.TwoTo256; 290 | } 291 | 292 | [Benchmark] 293 | public Int256 Multiply_Int256() 294 | { 295 | Int256.Multiply(A.Item2, B.Item2, out Int256 res); 296 | return res; 297 | } 298 | } 299 | 300 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 301 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 302 | [MemoryDiagnoser] 303 | public class MultiplyModUnsigned : UnsignedThreeParamBenchmarkBase 304 | { 305 | [Benchmark(Baseline = true)] 306 | public BigInteger MultiplyMod_BigInteger() 307 | { 308 | return ((A.Item1 * B.Item1) % C.Item1); 309 | } 310 | 311 | [Benchmark] 312 | public UInt256 MultiplyMod_UInt256() 313 | { 314 | UInt256.MultiplyMod(A.Item2, B.Item2, C.Item2, out UInt256 res); 315 | return res; 316 | } 317 | } 318 | 319 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 320 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 321 | [MemoryDiagnoser] 322 | public class MultiplyModSigned : SignedThreeParamBenchmarkBase 323 | { 324 | [Benchmark(Baseline = true)] 325 | public BigInteger MultiplyMod_BigInteger() 326 | { 327 | return ((A.Item1 * B.Item1) % C.Item1); 328 | } 329 | 330 | [Benchmark] 331 | public Int256 MultiplyMod_Int256() 332 | { 333 | Int256.MultiplyMod(A.Item2, B.Item2, C.Item2, out Int256 res); 334 | return res; 335 | } 336 | } 337 | 338 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 339 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 340 | [MemoryDiagnoser] 341 | public class DivideUnsigned : UnsignedTwoParamBenchmarkBase 342 | { 343 | [Benchmark(Baseline = true)] 344 | public BigInteger Divide_BigInteger() 345 | { 346 | return (A.Item1 / B.Item1); 347 | } 348 | 349 | [Benchmark] 350 | public UInt256 Divide_UInt256() 351 | { 352 | UInt256.Divide(A.Item2, B.Item2, out UInt256 res); 353 | return res; 354 | } 355 | } 356 | 357 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 358 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 359 | [MemoryDiagnoser] 360 | public class DivideSigned : SignedTwoParamBenchmarkBase 361 | { 362 | [Benchmark(Baseline = true)] 363 | public BigInteger Divide_BigInteger() 364 | { 365 | return (A.Item1 / B.Item1); 366 | } 367 | 368 | [Benchmark] 369 | public Int256 Divide_Int256() 370 | { 371 | Int256.Divide(A.Item2, B.Item2, out Int256 res); 372 | return res; 373 | } 374 | } 375 | 376 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 377 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 378 | [MemoryDiagnoser] 379 | public class ExpUnsigned : UnsignedIntTwoParamBenchmarkBase 380 | { 381 | [Benchmark(Baseline = true)] 382 | public BigInteger Exp_BigInteger() 383 | { 384 | return BigInteger.ModPow(A.Item1, D.Item1, Numbers.TwoTo256); 385 | } 386 | 387 | [Benchmark] 388 | public UInt256 Exp_UInt256() 389 | { 390 | UInt256.Exp(A.Item2, D.Item2, out UInt256 res); 391 | return res; 392 | } 393 | } 394 | 395 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 396 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 397 | [MemoryDiagnoser] 398 | public class ExpSigned : SignedIntTwoParamBenchmarkBase 399 | { 400 | [Benchmark(Baseline = true)] 401 | public BigInteger Exp_BigInteger() 402 | { 403 | return BigInteger.ModPow(A.Item1, D.Item1, Numbers.TwoTo256); 404 | } 405 | 406 | [Benchmark] 407 | public Int256 Exp_Int256() 408 | { 409 | Int256.Exp(A.Item2, D.Item2, out Int256 res); 410 | return res; 411 | } 412 | } 413 | 414 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 415 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 416 | [MemoryDiagnoser] 417 | public class ExpModUnsigned : UnsignedThreeParamBenchmarkBase 418 | { 419 | [Benchmark(Baseline = true)] 420 | public BigInteger ExpMod_BigInteger() 421 | { 422 | return BigInteger.ModPow(A.Item1, B.Item1, C.Item1); 423 | } 424 | 425 | [Benchmark] 426 | public UInt256 ExpMod_UInt256() 427 | { 428 | UInt256.ExpMod(A.Item2, B.Item2, C.Item2, out UInt256 res); 429 | return res; 430 | } 431 | } 432 | 433 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 434 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 435 | [MemoryDiagnoser] 436 | public class ExpModSigned : SignedBenchmarkBase 437 | { 438 | [ParamsSource(nameof(ValuesTuple))] 439 | public (BigInteger, Int256) A; 440 | 441 | [ParamsSource(nameof(ValuesTuplePositive))] 442 | public (BigInteger, Int256) B; 443 | 444 | [ParamsSource(nameof(ValuesTuplePositive))] 445 | public (BigInteger, Int256) C; 446 | 447 | [Benchmark(Baseline = true)] 448 | public BigInteger ExpMod_BigInteger() 449 | { 450 | return BigInteger.ModPow(A.Item1, B.Item1, C.Item1); 451 | } 452 | 453 | [Benchmark] 454 | public Int256 ExpMod_Int256() 455 | { 456 | Int256.ExpMod(A.Item2, B.Item2, C.Item2, out Int256 res); 457 | return res; 458 | } 459 | } 460 | 461 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 462 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 463 | [MemoryDiagnoser] 464 | public class LeftShiftUnsigned : UnsignedIntTwoParamBenchmarkBase 465 | { 466 | [Benchmark(Baseline = true)] 467 | public BigInteger LeftShift_BigInteger() 468 | { 469 | return (A.Item1 << D.Item1) % Numbers.TwoTo256; 470 | } 471 | 472 | [Benchmark] 473 | public UInt256 LeftShift_UInt256() 474 | { 475 | A.Item2.LeftShift(D.Item1, out UInt256 res); 476 | return res; 477 | } 478 | } 479 | 480 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 481 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 482 | [MemoryDiagnoser] 483 | public class LeftShiftSigned : SignedIntTwoParamBenchmarkBase 484 | { 485 | [Benchmark(Baseline = true)] 486 | public BigInteger LeftShift_BigInteger() 487 | { 488 | return (A.Item1 << D.Item1) % Numbers.TwoTo256; 489 | } 490 | 491 | [Benchmark] 492 | public Int256 LeftShift_Int256() 493 | { 494 | A.Item2.LeftShift(D.Item1, out Int256 res); 495 | return res; 496 | } 497 | } 498 | 499 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 500 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 501 | [MemoryDiagnoser] 502 | public class RightShiftUnsigned : UnsignedIntTwoParamBenchmarkBase 503 | { 504 | [Benchmark(Baseline = true)] 505 | public BigInteger RightShift_BigInteger() 506 | { 507 | return (A.Item1 >> D.Item1) % Numbers.TwoTo256; 508 | } 509 | 510 | [Benchmark] 511 | public UInt256 RightShift_UInt256() 512 | { 513 | A.Item2.RightShift(D.Item1, out UInt256 res); 514 | return res; 515 | } 516 | } 517 | 518 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 519 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 520 | [MemoryDiagnoser] 521 | public class RightShiftSigned : SignedIntTwoParamBenchmarkBase 522 | { 523 | [Benchmark(Baseline = true)] 524 | public BigInteger RightShift_BigInteger() 525 | { 526 | return (A.Item1 >> D.Item1) % Numbers.TwoTo256; 527 | } 528 | 529 | [Benchmark] 530 | public Int256 RightShift_Int256() 531 | { 532 | A.Item2.RightShift(D.Item1, out Int256 res); 533 | return res; 534 | } 535 | } 536 | 537 | [SimpleJob(RuntimeMoniker.Net10_0, baseline: true)] 538 | [NoIntrinsicsJob(RuntimeMoniker.Net10_0)] 539 | [MemoryDiagnoser] 540 | public class IsZeroOne 541 | { 542 | public UInt256[] Values { get; } = { UInt256.Zero, UInt256.One, UInt256.MaxValue }; 543 | 544 | [ParamsSource(nameof(Values))] 545 | public UInt256 A; 546 | 547 | [Benchmark] 548 | public bool IsZero() 549 | { 550 | return A.IsZero; 551 | } 552 | 553 | [Benchmark] 554 | public bool IsOne() 555 | { 556 | return A.IsOne; 557 | } 558 | } 559 | } 560 | -------------------------------------------------------------------------------- /src/Nethermind.Int256/Int256.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.Numerics; 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("Nethermind.Int256.Tests")] 7 | 8 | namespace Nethermind.Int256 9 | { 10 | public readonly struct Int256 : IEquatable, IComparable, IComparable, IInteger, IConvertible 11 | { 12 | public static readonly Int256 Zero = (Int256)0UL; 13 | public static readonly Int256 One = (Int256)1UL; 14 | public static readonly Int256 MinusOne = -1L; 15 | public static readonly Int256 Max = new Int256(((BigInteger.One << 255) - 1)); 16 | 17 | internal readonly UInt256 _value; 18 | 19 | public Int256(ReadOnlySpan bytes, bool isBigEndian) 20 | { 21 | _value = new UInt256(bytes, isBigEndian); 22 | } 23 | 24 | public Int256(UInt256 value) 25 | { 26 | _value = value; 27 | } 28 | 29 | public Int256(BigInteger big) 30 | { 31 | if (big.Sign < 0) 32 | { 33 | (new Int256((UInt256)(-big))).Neg(out Int256 neg); 34 | _value = neg._value; 35 | } 36 | else 37 | { 38 | _value = (UInt256)big; 39 | } 40 | } 41 | 42 | public Int256(int n) 43 | { 44 | if (n < 0) 45 | { 46 | Int256 value = new(new UInt256((ulong)-n)); 47 | value.Neg(out Int256 res); 48 | _value = res._value; 49 | } 50 | else 51 | { 52 | _value = new UInt256((ulong)n); 53 | } 54 | } 55 | 56 | public static explicit operator Int256(int n) => new Int256(n); 57 | 58 | public Int256 OneValue => One; 59 | 60 | public Int256 ZeroValue => Zero; 61 | 62 | public int Sign => _value.IsZero ? 0 : _value.u3 < 0x8000000000000000ul ? 1 : -1; 63 | public bool IsNegative => Sign < 0; 64 | 65 | public static Int256 operator +(in Int256 a, in Int256 b) 66 | { 67 | Add(in a, in b, out Int256 res); 68 | return res; 69 | } 70 | 71 | public static bool AddOverflow(in Int256 a, in Int256 b, out Int256 res) 72 | { 73 | var overflow = UInt256.AddOverflow(a._value, b._value, out UInt256 ures); 74 | res = new Int256(ures); 75 | return overflow; 76 | } 77 | 78 | public static void Add(in Int256 a, in Int256 b, out Int256 res) 79 | { 80 | UInt256.AddOverflow(a._value, b._value, out UInt256 ures); 81 | res = new Int256(ures); 82 | } 83 | 84 | public void Add(in Int256 a, out Int256 res) => Add(this, a, out res); 85 | 86 | public static void AddMod(in Int256 x, in Int256 y, in Int256 m, out Int256 res) 87 | { 88 | Int256 mt = m; 89 | if (mt.IsOne) 90 | { 91 | res = Zero; 92 | return; 93 | } 94 | 95 | if (m.Sign < 0) 96 | { 97 | m.Neg(out mt); 98 | } 99 | int xSign = x.Sign; 100 | int ySign = y.Sign; 101 | if (xSign < 0 && ySign < 0) 102 | { 103 | x.Neg(out Int256 xNeg); 104 | y.Neg(out Int256 yNeg); 105 | xNeg._value.AddMod(yNeg._value, mt._value, out UInt256 ures); 106 | res = new Int256(ures); 107 | res.Neg(out res); 108 | } 109 | else if (xSign > 0 && ySign > 0) 110 | { 111 | x._value.AddMod(y._value, mt._value, out UInt256 ures); 112 | res = new Int256(ures); 113 | } 114 | else 115 | { 116 | x.Add(y, out res); 117 | res.Mod(mt, out res); 118 | } 119 | } 120 | 121 | public void AddMod(in Int256 a, in Int256 m, out Int256 res) => AddMod(this, a, m, out res); 122 | 123 | public static void Subtract(in Int256 a, in Int256 b, out Int256 res) 124 | { 125 | a._value.Subtract(b._value, out UInt256 ures); 126 | res = new Int256(ures); 127 | } 128 | 129 | public void Subtract(in Int256 a, out Int256 res) => Subtract(this, a, out res); 130 | 131 | public static void SubtractMod(in Int256 x, in Int256 y, in Int256 m, out Int256 res) 132 | { 133 | var mt = m; 134 | if (mt.IsOne) 135 | { 136 | res = Int256.Zero; 137 | return; 138 | } 139 | 140 | if (m.Sign < 0) 141 | { 142 | m.Neg(out mt); 143 | } 144 | int xSign = x.Sign; 145 | int ySign = y.Sign; 146 | if (xSign < 0 && ySign > 0) 147 | { 148 | x.Neg(out Int256 xNeg); 149 | xNeg._value.AddMod(y._value, mt._value, out UInt256 ures); 150 | res = new Int256(ures); 151 | res.Neg(out res); 152 | } 153 | else if (xSign > 0 && ySign < 0) 154 | { 155 | y.Neg(out Int256 yNeg); 156 | x._value.AddMod(yNeg._value, mt._value, out UInt256 ures); 157 | res = new Int256(ures); 158 | } 159 | else 160 | { 161 | x.Subtract(y, out res); 162 | res.Mod(mt, out res); 163 | } 164 | } 165 | 166 | public void SubtractMod(in Int256 a, in Int256 m, out Int256 res) => SubtractMod(this, a, m, out res); 167 | 168 | public static void Multiply(in Int256 a, in Int256 b, out Int256 res) 169 | { 170 | Int256 av = a, bv = b; 171 | int aSign = a.Sign; 172 | int bSign = b.Sign; 173 | if (aSign < 0) 174 | { 175 | a.Neg(out av); 176 | } 177 | if (bSign < 0) 178 | { 179 | b.Neg(out bv); 180 | } 181 | UInt256.Multiply(av._value, bv._value, out UInt256 ures); 182 | res = new Int256(ures); 183 | if ((aSign < 0 && bSign < 0) || (aSign >= 0 && bSign >= 0)) 184 | { 185 | return; 186 | } 187 | res.Neg(out res); 188 | } 189 | 190 | public void Multiply(in Int256 a, out Int256 res) => Multiply(this, a, out res); 191 | 192 | public static void MultiplyMod(in Int256 x, in Int256 y, in Int256 m, out Int256 res) 193 | { 194 | var mAbs = m; 195 | if (m.Sign < 0) 196 | { 197 | m.Neg(out mAbs); 198 | } 199 | int xSign = x.Sign; 200 | int ySign = y.Sign; 201 | if ((xSign < 0 && ySign >= 0) || (xSign >= 0 && ySign < 0)) 202 | { 203 | var xAbs = x; 204 | var yAbs = y; 205 | if (xSign < 0) 206 | { 207 | x.Neg(out xAbs); 208 | } 209 | else 210 | { 211 | y.Neg(out yAbs); 212 | } 213 | xAbs._value.MultiplyMod(yAbs._value, mAbs._value, out UInt256 ures); 214 | res = new Int256(ures); 215 | res.Neg(out res); 216 | } 217 | else 218 | { 219 | var xAbs = x; 220 | var yAbs = y; 221 | if (xSign < 0) 222 | { 223 | x.Neg(out xAbs); 224 | y.Neg(out yAbs); 225 | } 226 | xAbs._value.MultiplyMod(yAbs._value, mAbs._value, out UInt256 ures); 227 | res = new Int256(ures); 228 | } 229 | } 230 | 231 | public void MultiplyMod(in Int256 a, in Int256 m, out Int256 res) => MultiplyMod(this, a, m, out res); 232 | 233 | public static void Divide(in Int256 n, in Int256 d, out Int256 res) 234 | { 235 | UInt256 value; 236 | if (n.Sign >= 0) 237 | { 238 | if (d.Sign >= 0) 239 | { 240 | // pos / pos 241 | UInt256.Divide(n._value, d._value, out value); 242 | res = new Int256(value); 243 | return; 244 | } 245 | else 246 | { 247 | // pos / neg 248 | Neg(d, out Int256 neg); 249 | UInt256.Divide(n._value, neg._value, out value); 250 | res = new Int256(value); 251 | res.Neg(out res); 252 | return; 253 | } 254 | } 255 | 256 | Neg(n, out Int256 nNeg); 257 | if (d.Sign < 0) 258 | { 259 | // neg / neg 260 | Neg(d, out Int256 dNeg); 261 | UInt256.Divide(nNeg._value, dNeg._value, out value); 262 | res = new Int256(value); 263 | return; 264 | } 265 | // neg / pos 266 | UInt256.Divide(nNeg._value, d._value, out value); 267 | res = new Int256(value); 268 | res.Neg(out res); 269 | } 270 | 271 | public void Divide(in Int256 a, out Int256 res) => Divide(this, a, out res); 272 | 273 | public static void Exp(in Int256 b, in Int256 e, out Int256 res) 274 | { 275 | if (e.Sign < 0) 276 | { 277 | throw new ArgumentException("exponent must be non-negative"); 278 | } 279 | if (b.Sign < 0) 280 | { 281 | b.Neg(out Int256 neg); 282 | UInt256.Exp(neg._value, e._value, out UInt256 ures); 283 | if (!e._value.Bit(0)) 284 | { 285 | res = new Int256(ures); 286 | } 287 | else 288 | { 289 | res = new Int256(ures); 290 | res.Neg(out res); 291 | } 292 | } 293 | else 294 | { 295 | UInt256.Exp(b._value, e._value, out UInt256 ures); 296 | res = new Int256(ures); 297 | } 298 | } 299 | 300 | public void Exp(in Int256 exp, out Int256 res) => Exp(this, exp, out res); 301 | 302 | public static void ExpMod(in Int256 bs, in Int256 exp, in Int256 m, out Int256 res) 303 | { 304 | if (exp < Zero) 305 | { 306 | throw new ArgumentException("exponent must not be negative"); 307 | } 308 | Int256 bv = bs; 309 | bool switchSign = false; 310 | if (bs.Sign < 0) 311 | { 312 | bv.Neg(out bv); 313 | switchSign = exp._value.Bit(0); 314 | } 315 | var mAbs = m; 316 | if (mAbs.Sign < 0) 317 | { 318 | mAbs.Neg(out mAbs); 319 | } 320 | UInt256.ExpMod(bv._value, exp._value, mAbs._value, out UInt256 ures); 321 | res = new Int256(ures); 322 | if (switchSign) 323 | { 324 | res.Neg(out res); 325 | } 326 | } 327 | 328 | public void ExpMod(in Int256 exp, in Int256 m, out Int256 res) => ExpMod(this, exp, m, out res); 329 | 330 | public static void LeftShift(in Int256 x, int n, out Int256 res) 331 | { 332 | x._value.LeftShift(n, out UInt256 ures); 333 | res = new Int256(ures); 334 | } 335 | 336 | // Mod sets res to (sign x) * { abs(x) modulus abs(y) } 337 | // If y == 0, z is set to 0 (OBS: differs from the big.Int) 338 | public static void Mod(in Int256 x, in Int256 y, out Int256 res) 339 | { 340 | Int256 xIn = x, yIn = y; 341 | int xs = x.Sign; 342 | 343 | // abs x 344 | if (xs == -1) 345 | { 346 | Neg(x, out xIn); 347 | } 348 | // abs y 349 | if (y.Sign == -1) 350 | { 351 | Neg(y, out yIn); 352 | } 353 | UInt256.Mod((UInt256)xIn, (UInt256)yIn, out UInt256 value); 354 | res = new Int256(value); 355 | if (xs == -1) 356 | { 357 | Neg(res, out res); 358 | } 359 | } 360 | 361 | public void Mod(in Int256 m, out Int256 res) => Mod(this, m, out res); 362 | 363 | // Abs sets res to the absolute value 364 | // Abs(0) = 0 365 | // Abs(1) = 1 366 | // Abs(2**255) = -2**255 367 | // Abs(2**256-1) = -1 368 | public void Abs(out Int256 res) 369 | { 370 | if (Sign >= 0) 371 | { 372 | res = this; 373 | } 374 | else 375 | { 376 | Neg(this, out res); 377 | } 378 | } 379 | 380 | // Neg returns -x mod 2**256. 381 | public static void Neg(in Int256 x, out Int256 neg) 382 | { 383 | UInt256.Subtract(UInt256.Zero, x._value, out UInt256 value); 384 | neg = new Int256(value); 385 | } 386 | 387 | public void Neg(out Int256 res) => Neg(this, out res); 388 | 389 | public void LeftShift(int n, out Int256 res) => LeftShift(this, n, out res); 390 | 391 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 392 | private static long Rsh(long a, int n) 393 | { 394 | var n1 = n / 2; 395 | var n2 = n - n1; 396 | return (a >> n1) >> n2; 397 | } 398 | 399 | private void Srsh64(out Int256 res) 400 | { 401 | res = new Int256(new UInt256(_value.u1, _value.u2, _value.u3, ulong.MaxValue)); 402 | } 403 | 404 | private void Srsh128(out Int256 res) 405 | { 406 | res = new Int256(new UInt256(_value.u2, _value.u3, ulong.MaxValue, ulong.MaxValue)); 407 | } 408 | 409 | private void Srsh192(out Int256 res) 410 | { 411 | res = new Int256(new UInt256(_value.u3, ulong.MaxValue, ulong.MaxValue, ulong.MaxValue)); 412 | } 413 | 414 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 415 | private static void Rsh(in Int256 x, int n, out Int256 res) 416 | { 417 | if (x.Sign >= 0) 418 | { 419 | x._value.RightShift(n, out UInt256 ures); 420 | res = new Int256(ures); 421 | return; 422 | } 423 | if (n % 64 == 0) 424 | { 425 | switch (n) 426 | { 427 | case 0: 428 | res = x; 429 | return; 430 | case 64: 431 | x.Srsh64(out res); 432 | return; 433 | case 128: 434 | x.Srsh128(out res); 435 | return; 436 | case 192: 437 | x.Srsh192(out res); 438 | return; 439 | default: 440 | One.Neg(out res); 441 | return; 442 | } 443 | } 444 | res = x; 445 | ulong z0 = x._value.u0, z1 = x._value.u1, z2 = x._value.u2, z3 = x._value.u3; 446 | ulong a = UInt256.Lsh(ulong.MaxValue, 64 - (n % 64)); 447 | // Big swaps first 448 | if (n > 192) 449 | { 450 | if (n > 256) 451 | { 452 | One.Neg(out res); 453 | return; 454 | } 455 | x.Srsh192(out res); 456 | z0 = res._value.u0; 457 | z1 = res._value.u1; 458 | z2 = res._value.u2; 459 | z3 = res._value.u3; 460 | n -= 192; 461 | goto sh192; 462 | } 463 | else if (n > 128) 464 | { 465 | x.Srsh128(out res); 466 | z0 = res._value.u0; 467 | z1 = res._value.u1; 468 | z2 = res._value.u2; 469 | z3 = res._value.u3; 470 | n -= 128; 471 | goto sh128; 472 | } 473 | else if (n > 64) 474 | { 475 | x.Srsh64(out res); 476 | z0 = res._value.u0; 477 | z1 = res._value.u1; 478 | z2 = res._value.u2; 479 | z3 = res._value.u3; 480 | n -= 64; 481 | goto sh64; 482 | } 483 | else 484 | { 485 | res = x; 486 | z0 = res._value.u0; 487 | z1 = res._value.u1; 488 | z2 = res._value.u2; 489 | z3 = res._value.u3; 490 | } 491 | 492 | // remaining shifts 493 | z3 = UInt256.Rsh(res._value.u3, n) | a; 494 | a = UInt256.Lsh(res._value.u3, 64 - n); 495 | 496 | sh64: 497 | z2 = UInt256.Rsh(res._value.u2, n) | a; 498 | a = UInt256.Lsh(res._value.u2, 64 - n); 499 | 500 | sh128: 501 | z1 = UInt256.Rsh(res._value.u1, n) | a; 502 | a = UInt256.Lsh(res._value.u1, 64 - n); 503 | 504 | sh192: 505 | z0 = UInt256.Rsh(res._value.u0, n) | a; 506 | 507 | res = new Int256(new UInt256(z0, z1, z2, z3)); 508 | } 509 | 510 | public static void RightShift(in Int256 x, int n, out Int256 res) => Rsh(x, n, out res); 511 | 512 | public void RightShift(int n, out Int256 res) => RightShift(this, n, out res); 513 | 514 | public void Convert(out BigInteger big) 515 | { 516 | if (Sign < 0) 517 | { 518 | Abs(out Int256 res); 519 | res._value.Convert(out big); 520 | big = -big; 521 | } 522 | else 523 | { 524 | _value.Convert(out big); 525 | } 526 | } 527 | 528 | public override string ToString() 529 | { 530 | return ToString(null); 531 | } 532 | 533 | [OverloadResolutionPriority(1)] 534 | private bool Equals(in Int256 other) => _value.Equals(other._value); 535 | 536 | public bool Equals(Int256 other) => _value.Equals(other._value); 537 | 538 | public override bool Equals(object? obj) => obj is Int256 other && Equals(other); 539 | 540 | public override int GetHashCode() => _value.GetHashCode(); 541 | 542 | public static bool operator ==(in Int256 a, in Int256 b) => a.Equals(b); 543 | 544 | public static bool operator !=(in Int256 a, in Int256 b) => !(a == b); 545 | 546 | public bool IsZero => this == Zero; 547 | 548 | public bool IsOne => this == One; 549 | 550 | public Int256 MaximalValue => Max; 551 | 552 | public int CompareTo(object? obj) => obj is not Int256 int256 ? throw new InvalidOperationException() : CompareTo(int256); 553 | 554 | public int CompareTo(Int256 b) => this < b ? -1 : Equals(b) ? 0 : 1; 555 | 556 | public static explicit operator UInt256(Int256 z) => z._value; 557 | 558 | public static bool operator <(in Int256 z, in Int256 x) 559 | { 560 | int zSign = z.Sign; 561 | int xSign = x.Sign; 562 | 563 | if (zSign >= 0) 564 | { 565 | if (xSign < 0) 566 | { 567 | return false; 568 | } 569 | } 570 | else if (xSign >= 0) 571 | { 572 | return true; 573 | } 574 | 575 | return z._value < x._value; 576 | } 577 | public static bool operator >(in Int256 z, in Int256 x) => x < z; 578 | 579 | public static explicit operator Int256(ulong value) => new((UInt256)value); 580 | 581 | public static implicit operator Int256(long value) => new(value); 582 | 583 | public static explicit operator BigInteger(Int256 x) 584 | { 585 | Span bytes = stackalloc byte[32]; 586 | BinaryPrimitives.WriteUInt64LittleEndian(bytes.Slice(0, 8), x._value.u0); 587 | BinaryPrimitives.WriteUInt64LittleEndian(bytes.Slice(8, 8), x._value.u1); 588 | BinaryPrimitives.WriteUInt64LittleEndian(bytes.Slice(16, 8), x._value.u2); 589 | BinaryPrimitives.WriteUInt64LittleEndian(bytes.Slice(24, 8), x._value.u3); 590 | return new BigInteger(bytes); 591 | } 592 | 593 | public static explicit operator Int256(BigInteger big) => new(big); 594 | 595 | public TypeCode GetTypeCode() => TypeCode.Object; 596 | public bool ToBoolean(IFormatProvider? provider) => !IsZero; 597 | public byte ToByte(IFormatProvider? provider) => System.Convert.ToByte(ToDecimal(provider), provider); 598 | public char ToChar(IFormatProvider? provider) => System.Convert.ToChar(ToDecimal(provider), provider); 599 | public DateTime ToDateTime(IFormatProvider? provider) => System.Convert.ToDateTime(ToDecimal(provider), provider); 600 | public decimal ToDecimal(IFormatProvider? provider) => (decimal)(BigInteger)this; 601 | public double ToDouble(IFormatProvider? provider) => (double)(BigInteger)this; 602 | public short ToInt16(IFormatProvider? provider) => System.Convert.ToInt16(ToDecimal(provider), provider); 603 | public int ToInt32(IFormatProvider? provider) => System.Convert.ToInt32(ToDecimal(provider), provider); 604 | public long ToInt64(IFormatProvider? provider) => System.Convert.ToInt64(ToDecimal(provider), provider); 605 | public sbyte ToSByte(IFormatProvider? provider) => System.Convert.ToSByte(ToDecimal(provider), provider); 606 | public float ToSingle(IFormatProvider? provider) => (float)(BigInteger)this; 607 | public object ToType(Type conversionType, IFormatProvider? provider) => conversionType == typeof(BigInteger) 608 | ? (BigInteger)this 609 | : System.Convert.ChangeType(ToDecimal(provider), conversionType, provider); 610 | public ushort ToUInt16(IFormatProvider? provider) => System.Convert.ToUInt16(ToDecimal(provider), provider); 611 | public uint ToUInt32(IFormatProvider? provider) => System.Convert.ToUInt32(ToDecimal(provider), provider); 612 | public ulong ToUInt64(IFormatProvider? provider) => System.Convert.ToUInt64(ToDecimal(provider), provider); 613 | 614 | public string ToString(IFormatProvider? provider) 615 | { 616 | if (IsNegative) 617 | { 618 | Neg(out Int256 res); 619 | return "-" + res._value.ToString(provider); 620 | } 621 | return _value.ToString(provider); 622 | } 623 | 624 | public static void And(in Int256 a, in Int256 b, out Int256 res) 625 | { 626 | UInt256.And(in a._value, in b._value, out var o); 627 | res = new Int256(o); 628 | } 629 | 630 | public static void Xor(in Int256 a, in Int256 b, out Int256 res) 631 | { 632 | UInt256.Xor(in a._value, in b._value, out var o); 633 | res = new Int256(o); 634 | } 635 | 636 | public static void Or(in Int256 a, in Int256 b, out Int256 res) 637 | { 638 | UInt256.Or(in a._value, in b._value, out var o); 639 | res = new Int256(o); 640 | } 641 | 642 | public static void Not(in Int256 a, out Int256 res) 643 | { 644 | UInt256.Not(in a._value, out var o); 645 | res = new Int256(o); 646 | } 647 | } 648 | } 649 | -------------------------------------------------------------------------------- /src/Nethermind.Int256.Tests/UInt256Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Numerics; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | 7 | namespace Nethermind.Int256.Test 8 | { 9 | public abstract class UInt256TestsTemplate where T : IInteger 10 | { 11 | protected readonly Func convert; 12 | protected readonly Func convertFromInt; 13 | protected readonly Func postprocess; 14 | protected readonly BigInteger maxValue; 15 | 16 | protected UInt256TestsTemplate(Func convert, Func convertFromInt, Func postprocess, BigInteger maxValue) 17 | { 18 | this.convert = convert; 19 | this.convertFromInt = convertFromInt; 20 | this.postprocess = postprocess; 21 | this.maxValue = maxValue; 22 | } 23 | 24 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 25 | public virtual void Add((BigInteger A, BigInteger B) test) 26 | { 27 | BigInteger resBigInt = test.A + test.B; 28 | resBigInt %= (BigInteger.One << 256); 29 | resBigInt = postprocess(resBigInt); 30 | 31 | T a = convert(test.A); 32 | T b = convert(test.B); 33 | a.Add(b, out T res); 34 | res.Convert(out BigInteger resUInt256); 35 | 36 | resUInt256.Should().Be(resBigInt); 37 | 38 | // Test reusing input as output 39 | a.Add(b, out a); 40 | a.Convert(out resUInt256); 41 | resUInt256.Should().Be(resBigInt); 42 | 43 | a = convert(test.A); 44 | 45 | a.Add(b, out b); 46 | b.Convert(out resUInt256); 47 | resUInt256.Should().Be(resBigInt); 48 | } 49 | 50 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 51 | public virtual void AddOverflow((BigInteger A, BigInteger B) test) 52 | { 53 | BigInteger resUInt256; 54 | BigInteger resBigInt = test.A + test.B; 55 | bool expectedOverflow = test.A + test.B > (BigInteger)UInt256.MaxValue; 56 | 57 | resBigInt %= (BigInteger.One << 256); 58 | resBigInt = postprocess(resBigInt); 59 | T uint256a = convert(test.A); 60 | T uint256b = convert(test.B); 61 | 62 | bool overflow = T.AddOverflow(uint256a, uint256b, out T res); 63 | res.Convert(out resUInt256); 64 | 65 | resUInt256.Should().Be(resBigInt); 66 | overflow.Should().Be(expectedOverflow); 67 | 68 | // Test reusing input as output 69 | overflow = T.AddOverflow(uint256a, uint256b, out uint256a); 70 | uint256a.Convert(out resUInt256); 71 | 72 | resUInt256.Should().Be(resBigInt); 73 | overflow.Should().Be(expectedOverflow); 74 | 75 | uint256a = convert(test.A); 76 | 77 | overflow = T.AddOverflow(uint256a, uint256b, out uint256b); 78 | uint256b.Convert(out resUInt256); 79 | 80 | resUInt256.Should().Be(resBigInt); 81 | overflow.Should().Be(expectedOverflow); 82 | } 83 | 84 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] 85 | public virtual void AddMod((BigInteger A, BigInteger B, BigInteger M) test) 86 | { 87 | if (test.M.IsZero) 88 | { 89 | return; 90 | } 91 | BigInteger resBigInt = (test.A + test.B) % test.M; 92 | resBigInt %= (BigInteger.One << 256); 93 | resBigInt = postprocess(resBigInt); 94 | 95 | T uint256a = convert(test.A); 96 | T uint256b = convert(test.B); 97 | T uint256m = convert(test.M); 98 | uint256a.AddMod(uint256b, uint256m, out T res); 99 | res.Convert(out BigInteger resUInt256); 100 | 101 | resUInt256.Should().Be(resBigInt); 102 | 103 | // Test reusing input as output 104 | uint256a.AddMod(uint256b, uint256m, out uint256a); 105 | uint256a.Convert(out resUInt256); 106 | 107 | resUInt256.Should().Be(resBigInt); 108 | 109 | uint256a = convert(test.A); 110 | 111 | uint256a.AddMod(uint256b, uint256m, out uint256b); 112 | uint256b.Convert(out resUInt256); 113 | 114 | resUInt256.Should().Be(resBigInt); 115 | 116 | uint256b = convert(test.B); 117 | 118 | uint256a.AddMod(uint256b, uint256m, out uint256m); 119 | uint256m.Convert(out resUInt256); 120 | 121 | resUInt256.Should().Be(resBigInt); 122 | } 123 | 124 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 125 | public virtual void Subtract((BigInteger A, BigInteger B) test) 126 | { 127 | if (test.A < test.B) 128 | { 129 | return; 130 | } 131 | 132 | BigInteger resBigInt = test.A - test.B; 133 | resBigInt %= BigInteger.One << 256; 134 | resBigInt = postprocess(resBigInt); 135 | 136 | T uint256a = convert(test.A); 137 | T uint256b = convert(test.B); 138 | uint256a.Subtract(uint256b, out T res); 139 | res.Convert(out BigInteger resUInt256); 140 | 141 | resUInt256.Should().Be(resBigInt); 142 | 143 | // Test reusing input as output 144 | uint256a.Subtract(uint256b, out uint256a); 145 | uint256a.Convert(out resUInt256); 146 | 147 | resUInt256.Should().Be(resBigInt); 148 | 149 | uint256a = convert(test.A); 150 | 151 | uint256a.Subtract(uint256b, out uint256b); 152 | uint256b.Convert(out resUInt256); 153 | 154 | resUInt256.Should().Be(resBigInt); 155 | } 156 | 157 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] 158 | public virtual void SubtractMod((BigInteger A, BigInteger B, BigInteger M) test) 159 | { 160 | SubtractModCore(test, true); 161 | } 162 | 163 | protected void SubtractModCore((BigInteger A, BigInteger B, BigInteger M) test, bool convertToUnsigned) 164 | { 165 | if (test.M.IsZero) 166 | { 167 | return; 168 | } 169 | BigInteger resBigInt = (test.A - test.B) % test.M; 170 | 171 | // convert to unsigned modular value 172 | if (convertToUnsigned && resBigInt.Sign == -1) 173 | { 174 | resBigInt = test.M + resBigInt; 175 | } 176 | 177 | resBigInt %= (BigInteger.One << 256); 178 | resBigInt = postprocess(resBigInt); 179 | 180 | T uint256a = convert(test.A); 181 | T uint256b = convert(test.B); 182 | T uint256m = convert(test.M); 183 | uint256a.SubtractMod(uint256b, uint256m, out T res); 184 | res.Convert(out BigInteger resUInt256); 185 | 186 | resUInt256.Should().Be(resBigInt); 187 | 188 | // Test reusing input as output 189 | uint256a.SubtractMod(uint256b, uint256m, out uint256a); 190 | uint256a.Convert(out resUInt256); 191 | 192 | resUInt256.Should().Be(resBigInt); 193 | 194 | uint256a = convert(test.A); 195 | 196 | uint256a.SubtractMod(uint256b, uint256m, out uint256b); 197 | uint256b.Convert(out resUInt256); 198 | 199 | resUInt256.Should().Be(resBigInt); 200 | 201 | uint256b = convert(test.B); 202 | 203 | uint256a.SubtractMod(uint256b, uint256m, out uint256m); 204 | uint256m.Convert(out resUInt256); 205 | 206 | resUInt256.Should().Be(resBigInt); 207 | } 208 | 209 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 210 | public virtual void SubtractOverflow((BigInteger A, BigInteger B) test) 211 | { 212 | BigInteger resBigInt = test.A - test.B; 213 | resBigInt %= BigInteger.One << 256; 214 | resBigInt = postprocess(resBigInt); 215 | T uint256a = convert(test.A); 216 | T uint256b = convert(test.B); 217 | 218 | SubtractTest(in uint256a, in uint256b, out T res); 219 | 220 | // Test reusing input as output 221 | SubtractTest(in uint256a, in uint256b, out uint256a); 222 | 223 | uint256a = convert(test.A); 224 | 225 | SubtractTest(in uint256a, in uint256b, out uint256b); 226 | 227 | void SubtractTest(in T uint256a, in T uint256b, out T res) 228 | { 229 | BigInteger resUInt256; 230 | if (test.A >= test.B) 231 | { 232 | if (uint256a is UInt256 a && uint256b is UInt256 b) 233 | { 234 | res = (T)(object)(a - b); 235 | res.Convert(out resUInt256); 236 | } 237 | else 238 | { 239 | uint256a.Subtract(uint256b, out res); 240 | res.Convert(out resUInt256); 241 | } 242 | resUInt256.Should().Be(resBigInt); 243 | } 244 | else 245 | { 246 | if (uint256a is UInt256 a && uint256b is UInt256 b) 247 | { 248 | res = default!; 249 | a.Invoking(y => y - b) 250 | .Should().Throw() 251 | .WithMessage($"Underflow in subtraction {a} - {b}"); 252 | } 253 | else 254 | { 255 | uint256a.Subtract(uint256b, out res); 256 | res.Convert(out resUInt256); 257 | resUInt256.Should().Be(resBigInt); 258 | } 259 | } 260 | } 261 | } 262 | 263 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 264 | public virtual void Multiply((BigInteger A, BigInteger B) test) 265 | { 266 | BigInteger resBigInt = (test.A * test.B) % (BigInteger.One << 256); 267 | resBigInt = postprocess(resBigInt); 268 | 269 | T uint256a = convert(test.A); 270 | T uint256b = convert(test.B); 271 | uint256a.Multiply(uint256b, out T res); 272 | res.Convert(out BigInteger resUInt256); 273 | 274 | resUInt256.Should().Be(resBigInt); 275 | 276 | // Test reusing input as output 277 | uint256a.Multiply(uint256b, out uint256a); 278 | uint256a.Convert(out resUInt256); 279 | 280 | resUInt256.Should().Be(resBigInt); 281 | 282 | uint256a = convert(test.A); 283 | 284 | uint256a.Multiply(uint256b, out uint256b); 285 | uint256b.Convert(out resUInt256); 286 | 287 | resUInt256.Should().Be(resBigInt); 288 | } 289 | 290 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] 291 | public virtual void MultiplyMod((BigInteger A, BigInteger B, BigInteger M) test) 292 | { 293 | if (test.M.IsZero) 294 | { 295 | return; 296 | } 297 | BigInteger resBigInt = ((test.A * test.B) % test.M) % (BigInteger.One << 256); 298 | resBigInt = postprocess(resBigInt); 299 | 300 | T uint256a = convert(test.A); 301 | T uint256b = convert(test.B); 302 | T uint256m = convert(test.M); 303 | uint256a.MultiplyMod(uint256b, uint256m, out T res); 304 | res.Convert(out BigInteger resUInt256); 305 | 306 | resUInt256.Should().Be(resBigInt); 307 | 308 | // Test reusing input as output 309 | uint256a.MultiplyMod(uint256b, uint256m, out uint256a); 310 | uint256a.Convert(out resUInt256); 311 | 312 | resUInt256.Should().Be(resBigInt); 313 | 314 | uint256a = convert(test.A); 315 | uint256a.MultiplyMod(uint256b, uint256m, out uint256b); 316 | uint256b.Convert(out resUInt256); 317 | 318 | resUInt256.Should().Be(resBigInt); 319 | 320 | uint256b = convert(test.B); 321 | uint256a.MultiplyMod(uint256b, uint256m, out uint256m); 322 | uint256m.Convert(out resUInt256); 323 | 324 | resUInt256.Should().Be(resBigInt); 325 | } 326 | 327 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 328 | public virtual void Div((BigInteger A, BigInteger B) test) 329 | { 330 | if (test.B.IsZero) 331 | { 332 | return; 333 | } 334 | BigInteger resBigInt = (test.A / test.B) % (BigInteger.One << 256); 335 | resBigInt = postprocess(resBigInt); 336 | 337 | T uint256a = convert(test.A); 338 | T uint256b = convert(test.B); 339 | uint256a.Divide(uint256b, out T res); 340 | res.Convert(out BigInteger resUInt256); 341 | 342 | resUInt256.Should().Be(resBigInt); 343 | 344 | // Test reusing input as output 345 | uint256a.Divide(uint256b, out uint256a); 346 | uint256a.Convert(out resUInt256); 347 | 348 | resUInt256.Should().Be(resBigInt); 349 | 350 | uint256a = convert(test.A); 351 | uint256a.Divide(uint256b, out uint256b); 352 | uint256b.Convert(out resUInt256); 353 | 354 | resUInt256.Should().Be(resBigInt); 355 | } 356 | 357 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 358 | public virtual void And((BigInteger A, BigInteger B) test) 359 | { 360 | BigInteger resBigInt = test.A & test.B; 361 | resBigInt = postprocess(resBigInt); 362 | 363 | T uint256a = convert(test.A); 364 | T uint256b = convert(test.B); 365 | T.And(uint256a, uint256b, out T res); 366 | res.Convert(out BigInteger resUInt256); 367 | 368 | resUInt256.Should().Be(resBigInt); 369 | 370 | // Test reusing input as output 371 | T.And(uint256a, uint256b, out uint256a); 372 | uint256a.Convert(out resUInt256); 373 | 374 | resUInt256.Should().Be(resBigInt); 375 | 376 | uint256a = convert(test.A); 377 | T.And(uint256a, uint256b, out uint256b); 378 | uint256b.Convert(out resUInt256); 379 | 380 | resUInt256.Should().Be(resBigInt); 381 | } 382 | 383 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 384 | public virtual void Or((BigInteger A, BigInteger B) test) 385 | { 386 | BigInteger resBigInt = test.A | test.B; 387 | resBigInt = postprocess(resBigInt); 388 | 389 | T uint256a = convert(test.A); 390 | T uint256b = convert(test.B); 391 | T.Or(uint256a, uint256b, out T res); 392 | res.Convert(out BigInteger resUInt256); 393 | 394 | resUInt256.Should().Be(resBigInt); 395 | 396 | // Test reusing input as output 397 | T.Or(uint256a, uint256b, out uint256a); 398 | uint256a.Convert(out resUInt256); 399 | 400 | resUInt256.Should().Be(resBigInt); 401 | 402 | uint256a = convert(test.A); 403 | T.Or(uint256a, uint256b, out uint256b); 404 | uint256b.Convert(out resUInt256); 405 | 406 | resUInt256.Should().Be(resBigInt); 407 | } 408 | 409 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 410 | public virtual void Xor((BigInteger A, BigInteger B) test) 411 | { 412 | BigInteger resBigInt = test.A ^ test.B; 413 | resBigInt = postprocess(resBigInt); 414 | 415 | T uint256a = convert(test.A); 416 | T uint256b = convert(test.B); 417 | T.Xor(uint256a, uint256b, out T res); 418 | res.Convert(out BigInteger resUInt256); 419 | 420 | resUInt256.Should().Be(resBigInt); 421 | 422 | // Test reusing input as output 423 | T.Xor(uint256a, uint256b, out uint256a); 424 | uint256a.Convert(out resUInt256); 425 | 426 | resUInt256.Should().Be(resBigInt); 427 | 428 | uint256a = convert(test.A); 429 | 430 | T.Xor(uint256a, uint256b, out uint256b); 431 | uint256b.Convert(out resUInt256); 432 | 433 | resUInt256.Should().Be(resBigInt); 434 | } 435 | 436 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] 437 | public virtual void Exp((BigInteger A, int n) test) 438 | { 439 | BigInteger resBigInt = BigInteger.Pow(test.A, test.n); 440 | resBigInt %= (BigInteger.One << 256); 441 | resBigInt = postprocess(resBigInt); 442 | 443 | T uint256a = convert(test.A); 444 | 445 | uint256a.Exp(convertFromInt(test.n), out T res); 446 | res.Convert(out BigInteger resUInt256); 447 | 448 | resUInt256.Should().Be(resBigInt); 449 | 450 | // Test reusing input as output 451 | uint256a.Exp(convertFromInt(test.n), out uint256a); 452 | res.Convert(out resUInt256); 453 | 454 | resUInt256.Should().Be(resBigInt); 455 | } 456 | 457 | [TestCaseSource(typeof(TernaryOps), nameof(TernaryOps.TestCases))] 458 | public virtual void ExpMod((BigInteger A, BigInteger B, BigInteger M) test) 459 | { 460 | if (test.M.IsZero || test.B < 0) 461 | { 462 | return; 463 | } 464 | BigInteger resBigInt = BigInteger.ModPow(test.A, test.B, test.M); 465 | resBigInt %= (BigInteger.One << 256); 466 | resBigInt = postprocess(resBigInt); 467 | 468 | T uint256a = convert(test.A); 469 | T uint256b = convert(test.B); 470 | T uint256m = convert(test.M); 471 | 472 | uint256a.ExpMod(uint256b, uint256m, out T res); 473 | res.Convert(out BigInteger resUInt256); 474 | 475 | resUInt256.Should().Be(resBigInt); 476 | 477 | // Test reusing input as output 478 | uint256a.ExpMod(uint256b, uint256m, out uint256a); 479 | uint256a.Convert(out resUInt256); 480 | 481 | resUInt256.Should().Be(resBigInt); 482 | 483 | uint256a = convert(test.A); 484 | uint256a.ExpMod(uint256b, uint256m, out uint256b); 485 | uint256b.Convert(out resUInt256); 486 | 487 | resUInt256.Should().Be(resBigInt); 488 | 489 | uint256b = convert(test.B); 490 | uint256a.ExpMod(uint256b, uint256m, out uint256m); 491 | uint256m.Convert(out resUInt256); 492 | 493 | resUInt256.Should().Be(resBigInt); 494 | } 495 | 496 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] 497 | public virtual void Lsh((BigInteger A, int n) test) 498 | { 499 | if (test.n == 0) 500 | { 501 | return; 502 | } 503 | BigInteger resBigInt = test.A << test.n; 504 | resBigInt %= (BigInteger.One << 256); 505 | resBigInt = postprocess(resBigInt); 506 | 507 | T uint256a = convert(test.A); 508 | uint256a.LeftShift(test.n, out T res); 509 | res.Convert(out BigInteger resUInt256); 510 | 511 | resUInt256.Should().Be(resBigInt); 512 | 513 | // Test reusing input as output 514 | uint256a.LeftShift(test.n, out uint256a); 515 | uint256a.Convert(out resUInt256); 516 | 517 | resUInt256.Should().Be(resBigInt); 518 | } 519 | 520 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.ShiftTestCases))] 521 | public virtual void Rsh((BigInteger A, int n) test) 522 | { 523 | if (test.n == 0) 524 | { 525 | return; 526 | } 527 | BigInteger resBigInt = test.A >> test.n; 528 | resBigInt %= (BigInteger.One << 256); 529 | resBigInt = postprocess(resBigInt); 530 | 531 | T uint256a = convert(test.A); 532 | uint256a.RightShift(test.n, out T res); 533 | res.Convert(out BigInteger resUInt256); 534 | 535 | resUInt256.Should().Be(resBigInt); 536 | 537 | // Test reusing input as output 538 | uint256a.RightShift(test.n, out uint256a); 539 | uint256a.Convert(out resUInt256); 540 | 541 | resUInt256.Should().Be(resBigInt); 542 | } 543 | 544 | [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.TestCases))] 545 | public virtual void ToBigIntegerAndBack(BigInteger test) 546 | { 547 | T uint256 = convert(test); 548 | uint256.Convert(out BigInteger res); 549 | res.Should().Be(test); 550 | } 551 | 552 | [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.TestCases))] 553 | public virtual void Not(BigInteger test) 554 | { 555 | BigInteger resBigInt = ~test; 556 | resBigInt %= (BigInteger.One << 256); 557 | if (typeof(T) == typeof(UInt256)) 558 | { 559 | ((Int256)resBigInt)._value.Convert(out resBigInt); 560 | } 561 | T uint256 = convert(test); 562 | T.Not(uint256, out T res); 563 | res.Convert(out BigInteger resUInt256); 564 | resUInt256.Should().Be(resBigInt); 565 | 566 | // Test reusing input as output 567 | T.Not(uint256, out uint256); 568 | uint256.Convert(out resUInt256); 569 | resUInt256.Should().Be(resBigInt); 570 | } 571 | 572 | [TestCaseSource(typeof(UnaryOps), nameof(UnaryOps.TestCases))] 573 | public virtual void ToString(BigInteger test) 574 | { 575 | T uint256 = convert(test); 576 | uint256.ToString().Should().Be(test.ToString()); 577 | } 578 | } 579 | 580 | [Parallelizable(ParallelScope.All)] 581 | public class UInt256Tests : UInt256TestsTemplate 582 | { 583 | public UInt256Tests() : base((BigInteger x) => (UInt256)x, (int x) => (UInt256)x, x => x, TestNumbers.UInt256Max) { } 584 | 585 | [Test] 586 | public virtual void Zero_is_min_value() 587 | { 588 | convert(1).ZeroValue.Should().Be(UInt256.MinValue); 589 | } 590 | 591 | [Test] 592 | public virtual void Zero_is_zero() 593 | { 594 | convert(1).ZeroValue.Should().Be(convert(BigInteger.Zero)); 595 | } 596 | 597 | [Test] 598 | public virtual void Is_zero() 599 | { 600 | convert(1).ZeroValue.IsZero.Should().BeTrue(); 601 | UInt256.One.IsZero.Should().BeFalse(); 602 | } 603 | 604 | [Test] 605 | public virtual void One_is_one() 606 | { 607 | convert(1).OneValue.Should().Be(convert(BigInteger.One)); 608 | } 609 | 610 | [Test] 611 | public virtual void Is_one() 612 | { 613 | convert(1).OneValue.IsOne.Should().BeTrue(); 614 | convert(1).ZeroValue.IsOne.Should().BeFalse(); 615 | } 616 | 617 | [Test] 618 | public virtual void Max_value_is_correct() 619 | { 620 | convert(1).MaximalValue.Should().Be(convert(maxValue)); 621 | } 622 | 623 | [Test] 624 | public virtual void ToBigEndian_And_Back() 625 | { 626 | byte[] bidEndian = convert(1000000000000000000).ToBigEndian(); 627 | new UInt256(bidEndian, true).Should().Be(convert(1000000000000000000)); 628 | } 629 | 630 | [Test] 631 | public virtual void ToLittleEndian_And_Back() 632 | { 633 | byte[] bidEndian = convert(1000000000000000000).ToLittleEndian(); 634 | new UInt256(bidEndian).Should().Be(convert(1000000000000000000)); 635 | } 636 | 637 | [Test] 638 | public virtual void Check_For_Correct_Big_And_Little_Endian() 639 | { 640 | byte[] bigEndianRepresentation = 641 | { 642 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 224, 182, 179, 167, 100, 643 | 0, 0 644 | }; 645 | convert(1000000000000000000).ToBigEndian().Should().BeEquivalentTo(bigEndianRepresentation); 646 | 647 | byte[] littleEndianRepresentation = 648 | { 649 | 0, 0, 100, 167, 179, 182, 224, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 650 | }; 651 | convert(1000000000000000000).ToLittleEndian().Should().BeEquivalentTo(littleEndianRepresentation); 652 | } 653 | 654 | [TestCaseSource(typeof(Convertibles), nameof(Convertibles.TestCases))] 655 | public void Convert(Type type, object value, Type expectedException, string expectedString) 656 | { 657 | string Expected(string valueString) 658 | { 659 | if (valueString.Contains("Infinity")) 660 | { 661 | return valueString.StartsWith('-') ? "-∞" : "∞"; 662 | } 663 | string expected = valueString.Replace(",", ""); 664 | return type == typeof(float) ? expected[..Math.Min(6, expected.Length)] : type == typeof(double) ? expected[..Math.Min(14, expected.Length)] : expected; 665 | } 666 | 667 | string valueString = value.ToString()!; 668 | if (!valueString.StartsWith('-')) 669 | { 670 | try 671 | { 672 | UInt256 item = (UInt256)BigInteger.Parse(valueString); 673 | string expected = expectedString ?? Expected(valueString); 674 | string convertedValue = Expected(((IFormattable)System.Convert.ChangeType(item, type)).ToString("0.#", null)); 675 | convertedValue.Should().BeEquivalentTo(expected); 676 | } 677 | catch (Exception e) when (e.GetType() == expectedException) { } 678 | } 679 | } 680 | [Test] 681 | public virtual void ParseLongNumber() 682 | { 683 | string hexValueWith66Zeroes = "0x" + new string('0', 66); 684 | BigInteger bigIntWith66Zeroes = BigInteger.Parse(hexValueWith66Zeroes.Substring(2), NumberStyles.HexNumber); 685 | var UintParsedValue = UInt256.Parse(hexValueWith66Zeroes); 686 | Assert.That((UInt256)bigIntWith66Zeroes, Is.EqualTo(UintParsedValue)); 687 | } 688 | 689 | [TestCaseSource(typeof(BinaryOps), nameof(BinaryOps.TestCases))] 690 | public void ComparisonOperators((BigInteger A, BigInteger B) test) 691 | { 692 | UInt256 uint256a = convert(test.A); 693 | UInt256 uint256b = convert(test.B); 694 | 695 | (uint256a < uint256b).Should().Be(test.A < test.B); 696 | (uint256a <= uint256b).Should().Be(test.A <= test.B); 697 | (uint256a > uint256b).Should().Be(test.A > test.B); 698 | (uint256a >= uint256b).Should().Be(test.A >= test.B); 699 | (uint256a == uint256b).Should().Be(test.A == test.B); 700 | 701 | // Test overloads ulong, long, uint, int 702 | if (test.A <= ulong.MaxValue) 703 | { 704 | ulong a = (ulong)test.A; 705 | 706 | (a < uint256b).Should().Be(a < test.B); 707 | (a <= uint256b).Should().Be(a <= test.B); 708 | (a > uint256b).Should().Be(a > test.B); 709 | (a >= uint256b).Should().Be(a >= test.B); 710 | (a == uint256b).Should().Be(a == test.B); 711 | } 712 | 713 | if (test.A <= long.MaxValue) 714 | { 715 | long a = (long)test.A; 716 | 717 | (a < uint256b).Should().Be(a < test.B); 718 | (a <= uint256b).Should().Be(a <= test.B); 719 | (a > uint256b).Should().Be(a > test.B); 720 | (a >= uint256b).Should().Be(a >= test.B); 721 | (a == uint256b).Should().Be(a == test.B); 722 | } 723 | 724 | if (test.A <= uint.MaxValue) 725 | { 726 | uint a = (uint)test.A; 727 | 728 | (a < uint256b).Should().Be(a < test.B); 729 | (a <= uint256b).Should().Be(a <= test.B); 730 | (a > uint256b).Should().Be(a > test.B); 731 | (a >= uint256b).Should().Be(a >= test.B); 732 | (a == uint256b).Should().Be(a == test.B); 733 | } 734 | 735 | if (test.A <= int.MaxValue) 736 | { 737 | int a = (int)test.A; 738 | 739 | (a < uint256b).Should().Be(a < test.B); 740 | (a <= uint256b).Should().Be(a <= test.B); 741 | (a > uint256b).Should().Be(a > test.B); 742 | (a >= uint256b).Should().Be(a >= test.B); 743 | (a == uint256b).Should().Be(a == test.B); 744 | } 745 | 746 | if (test.B <= ulong.MaxValue) 747 | { 748 | ulong b = (ulong)test.B; 749 | 750 | (uint256a < b).Should().Be(test.A < b); 751 | (uint256a <= b).Should().Be(test.A <= b); 752 | (uint256a > b).Should().Be(test.A > b); 753 | (uint256a >= b).Should().Be(test.A >= b); 754 | (uint256a == b).Should().Be(test.A == b); 755 | } 756 | 757 | if (test.B <= long.MaxValue) 758 | { 759 | long b = (long)test.B; 760 | 761 | (uint256a < b).Should().Be(test.A < b); 762 | (uint256a <= b).Should().Be(test.A <= b); 763 | (uint256a > b).Should().Be(test.A > b); 764 | (uint256a >= b).Should().Be(test.A >= b); 765 | (uint256a == b).Should().Be(test.A == b); 766 | } 767 | 768 | if (test.B <= uint.MaxValue) 769 | { 770 | uint b = (uint)test.B; 771 | 772 | (uint256a < b).Should().Be(test.A < b); 773 | (uint256a <= b).Should().Be(test.A <= b); 774 | (uint256a > b).Should().Be(test.A > b); 775 | (uint256a >= b).Should().Be(test.A >= b); 776 | (uint256a == b).Should().Be(test.A == b); 777 | } 778 | 779 | if (test.B <= int.MaxValue) 780 | { 781 | int b = (int)test.B; 782 | 783 | (uint256a < b).Should().Be(test.A < b); 784 | (uint256a <= b).Should().Be(test.A <= b); 785 | (uint256a > b).Should().Be(test.A > b); 786 | (uint256a >= b).Should().Be(test.A >= b); 787 | (uint256a == b).Should().Be(test.A == b); 788 | } 789 | } 790 | } 791 | } 792 | --------------------------------------------------------------------------------