├── .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 | [](https://github.com/nethermindeth/int256/actions/workflows/test-publish.yml)
4 | [](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 |
--------------------------------------------------------------------------------