diff --git a/internal/staging/primitives/uint256/README.md b/internal/staging/primitives/uint256/README.md new file mode 100644 index 0000000000..c7187ce0e3 --- /dev/null +++ b/internal/staging/primitives/uint256/README.md @@ -0,0 +1,272 @@ +uint256 +======= + +[![Build Status](https://github.com/decred/dcrd/workflows/Build%20and%20Test/badge.svg)](https://github.com/decred/dcrd/actions) +[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![Doc](https://img.shields.io/badge/doc-reference-blue.svg)](https://pkg.go.dev/github.com/decred/dcrd/internal/staging/primitives/uint256) + +## Fixed Precision Unsigned 256-bit Integer Arithmetic + +This package implements highly optimized allocation free fixed precision +unsigned 256-bit integer arithmetic. + +The following is a brief overview of the main features and benefits: + +- Strong focus on performance and correctness + - Every operation is faster than the stdlib `big.Int` equivalent and most + operations, including the primary math operations, are **significantly** faster + ([see performance comparison benchmarks](#uint256-performance-comparison)) +- Allocation free + - All non-formatting operations with the specialized type are allocation free +- Supports boolean comparison, bitwise logic, and bitwise shift operations +- All operations are performed modulo 2^256 +- Ergonomic API with unary-style arguments as well as some binary variants + - eg: `n.SetUint64(1).Lsh(64).SubUint64(1)` is equivalent to `n = 1<<64 - 1` + - eg: `n.Div2(n1, n2)` is equivalent to `n = n1/n2` +- Conversion-free support for interoperation with native `uint64` integers +- Direct conversion to and from little and big endian byte arrays +- Full support for formatted output and common base conversions + - Producing formatted output uses fewer allocations than `big.Int` +- 100% test coverage +- Comprehensive benchmarks + +The primary `big.Int` operations that are **NOT** currently provided: + +- Modular arithmetic with moduli other than 2^256 +- Signed arithmetic + - A caller could fairly easily implement it with 2's complement if desired + since `Negate` is provided +- Remainders from division + +These operations may be implemented in the future if the need arises. + +### Note About Standard Library `math/big.Int` Conversions + +This package provides convenience methods for converting to and from standard +library `big.Int`s, however, callers should make an active effort to avoid that +unless it is absolutely necessary as conversion to `big.Int` requires an +unavoidable allocation in most circumstances and `big.Int` is also slower for +every operation, often to a significant degree. Further, `big.Int`s often cause +further allocations while performing arithmetic, notably multiplication and +division. + +Regarding the aforementioned allocations, standard library `big.Int`s internally +require heap allocations to operate, so conversion to a new `big.Int` +necessarily causes an allocation. This means the `ToBig` method will always +incur at least one allocation. + +One feature of `big.Int`s is that they attempt to reuse their internal buffer +when possible, so the number of allocations due to conversion can sometimes be +reduced if reusing the same variable is an option. This package provides a +`PutBig` method which allows callers to reuse an existing `big.Int` for this +purpose. + +Do note however that even when reusing a `big.Int`, it will naturally still +require an allocation for the internal buffer unless it has already previously +allocated one large enough to be reused. + +## Categorized `Uint256` Summary + +This section summarizes the majority of the available operations provided by +category. See the full documentation for additional details about individual +methods and additional available methods. + +### Arithmetic Methods + +Operation | Native Equiv | Methods +----------------|--------------|------------------- +Add Assign | `n += x` | `Add`, `AddUint64` +Add | `n = x + y` | `Add2` +Subtract Assign | `n -= x` | `Sub`, `SubUint64` +Subtract | `n = x - y` | `Sub2` +Multiply Assign | `n *= x` | `Mul`, `MulUint64` +Multiply | `n = x * y` | `Mul2` +Divide Assign | `n /= x` | `Div` +Divide | `n = x / y` | `Div2` +Square Assign | `n *= n` | `Square` +Square | `n = x * x` | `SquareVal` +Negate Assign | `n = -n` | `Negate` +Negate | `n = -x` | `NegateVal` + +### Comparison Methods + +Operation | Native Equiv | Methods +-----------------|--------------|--------------------- +Equality | `x == y` | `Eq`, `EqUint64` +Less Than | `x < y` | `Lt`, `LtUint64` +Less Or Equal | `x <= y` | `LtEq`, `LtEqUint64` +Greater Than | `x > y` | `Gt`, `GtUint64` +Greater Or Equal | `x >= y` | `GtEq`, `GtEqUint64` +Comparison | n/a | `Cmp`, `CmpUint64` + +### Bitwise Methods + +Operation | Native Equiv | Method +-------------------|---------------|--------- +And Assign | `n &= x` | `And` +Or Assign | `n \|= x` | `Or` +Xor Assign | `n ^= x` | `Xor` +Not Assign | `n = ^x` | `Not` +Left Shift Assign | `n <<= x` | `Lsh` +Left Shift | `n = x << y` | `LshVal` +Right Shift Assign | `n >>= x` | `Rsh` +Right Shift | `n = x >> y` | `RshVal` +Bit Length | n/a | `BitLen` + +### Conversion Methods + +Operation | Methods +-------------------|----------------------------------------------- +From Big Endian | `SetBytes`, `SetByteSlice` +To Big Endian | `Bytes`, `PutBytes`, `PutBytesUnchecked` +From Little Endian | `SetBytesLE`, `SetByteSliceLE` +To Little Endian | `BytesLE`, `PutBytesLE`, `PutBytesUncheckedLE` +From `math/big.Int`| `SetBig` +To `math/big.Int` | `ToBig`, `PutBig` + +### Misc Convenience Methods + +Operation | Method +-----------------------------|----------- +Can represent with `uint32`? | `IsUint32` +Value modulo 2^32 | `Uint32` +Can represent with `uint64`? | `IsUint64` +Value modulo 2^64 | `Uint64` +Set to 0 | `Zero` +Is equal to zero? | `IsZero` +Is the value odd? | `IsOdd` + +### Output Formatting Methods + +Operation | Method +---------------------------------------------|--------- +Binary/Octal/Decimal/Hex | `Text` +Standard Formatted output (`fmt.Formatter`) | `Format` +Standard Unformatted output (`fmt.Stringer`) | `String` + +## Uint256 Performance Comparison + +The following benchmark results demonstrate the performance of most operations +as compared to standard library `big.Int`s. The benchmarks are from a Ryzen 7 +1700 processor and are the result of feeding `benchstat` 10 iterations of each. + +### Arithmetic Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +----------------|-------------------|-------------------|------------------- +Add | 158ns ± 2% | 2ns ± 1% | -98.67% +AddUint64 | 44.4ns ± 3% | 3.4ns ± 2% | -92.27% +Sub | 53.9ns ± 1% | 2.1ns ± 1% | -96.12% +SubUint64 | 44.8ns ± 1% | 3.4ns ± 2% | -92.37% +Mul | 419ns ± 1% | 10ns ± 2% | -97.64% +MulUint64 | 263ns ± 1% | 4ns ± 1% | -98.30% +Square | 418ns ± 0% | 7ns ± 2% | -98.39% +Div/num_lt_den | 75.4ns ± 1% | 3.4ns ± 1% | -95.51% +Div/num_eq_den | 253ns ± 2% | 4ns ± 3% | -98.56% +Div/1_by_1_near | 53.8ns ± 2% | 4.5ns ± 2% | -91.63% +Div/1_by_1_far | 31.4ns ± 2% | 14.6ns ± 2% | -53.64% +Div/2_by_1_near | 36.9ns ± 1% | 10.1ns ± 2% | -72.63% +Div/2_by_1_far | 49.1ns ± 1% | 28.8ns ± 1% | -41.29% +Div/3_by_1_near | 43.2ns ± 1% | 13.7ns ± 3% | -68.24% +Div/3_by_1_far | 57.0ns ± 1% | 43.6ns ± 1% | -23.59% +Div/4_by_1_near | 49.7ns ± 4% | 18.0ns ± 1% | -63.87% +Div/4_by_1_far | 65.2ns ± 4% | 57.8ns ± 2% | -11.41% +Div/2_by_2_near | 237ns ± 1% | 22ns ± 3% | -90.81% +Div/2_by_2_far | 237ns ± 1% | 30ns ± 3% | -87.17% +Div/3_by_2_near | 258ns ± 1% | 29ns ± 1% | -88.60% +Div/3_by_2_far | 257ns ± 1% | 50ns ± 2% | -80.42% +Div/4_by_2_near | 312ns ± 2% | 40ns ± 3% | -87.27% +Div/4_by_2_far | 310ns ± 1% | 71ns ± 3% | -77.19% +Div/3_by_3_near | 239ns ± 2% | 21ns ± 2% | -91.39% +Div/3_by_3_far | 242ns ± 4% | 33ns ± 3% | -86.33% +Div/4_by_3_near | 279ns ± 6% | 31ns ± 1% | -89.01% +Div/4_by_3_far | 271ns ± 1% | 46ns ± 3% | -82.99% +Div/4_by_4_near | 252ns ± 3% | 20ns ± 3% | -91.99% +Div/4_by_4_far | 249ns ± 2% | 36ns ± 2% | -85.65% +DivRandom | 202ns ± 1% | 23ns ± 1% | -88.43% +DivUint64 | 129ns ± 1% | 47ns ± 0% | -63.34% +Negate | 47.3ns ± 2% | 1.5ns ± 2% | -96.91% + +### Comparison Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +----------|-------------------|-------------------|------------------- +Eq | 12.7ns ± 1% | 2.1ns ± 1% | -83.72% +Lt | 12.6ns ± 1% | 3.0ns ± 1% | -75.96% +Gt | 12.6ns ± 1% | 3.0ns ± 1% | -75.91% +Cmp | 12.6ns ± 1% | 7.7ns ± 1% | -39.01% +CmpUint64 | 5.93ns ± 2% | 3.70ns ± 1% | -37.60% + +### Bitwise Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +----------------|-------------------|-------------------|------------------- +Lsh/bits_0 | 7.15ns ± 3% | 2.58ns ± 1% | -63.94% +Lsh/bits_1 | 14.8ns ± 1% | 4.2ns ± 1% | -71.40% +Lsh/bits_64 | 16.7ns ± 1% | 2.7ns ± 1% | -84.00% +Lsh/bits_128 | 16.9ns ± 2% | 2.7ns ± 0% | -84.21% +Lsh/bits_192 | 16.6ns ± 1% | 2.6ns ± 1% | -84.19% +Lsh/bits_255 | 16.3ns ± 2% | 2.8ns ± 2% | -83.11% +Lsh/bits_256 | 16.9ns ± 2% | 2.6ns ± 2% | -84.77% +Rsh/bits_0 | 8.76ns ± 2% | 2.57ns ± 1% | -70.63% +Rsh/bits_1 | 14.4ns ± 2% | 4.3ns ± 2% | -70.28% +Rsh/bits_64 | 12.8ns ± 1% | 2.9ns ± 2% | -77.31% +Rsh/bits_128 | 11.8ns ± 0% | 2.9ns ± 2% | -75.51% +Rsh/bits_192 | 10.5ns ± 2% | 2.6ns ± 1% | -75.17% +Rsh/bits_255 | 10.5ns ± 3% | 2.8ns ± 2% | -73.89% +Rsh/bits_256 | 5.50ns ± 1% | 2.58ns ± 2% | -53.15% +Not | 25.4ns ± 2% | 3.3ns ± 2% | -86.79% +Or | 17.9ns ± 5% | 3.4ns ± 6% | -80.94% +And | 16.7ns ± 2% | 3.4ns ± 0% | -79.93% +Xor | 17.9ns ± 1% | 3.4ns ± 2% | -80.91% +BitLen/bits_64 | 2.24ns ± 1% | 1.94ns ± 3% | -13.04% +BitLen/bits_128 | 2.25ns ± 2% | 1.96ns ± 2% | -13.17% +BitLen/bits_192 | 2.25ns ± 1% | 1.60ns ± 1% | -28.65% +BitLen/bits_255 | 2.26ns ± 2% | 1.61ns ± 1% | -29.04% + +### Conversion Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +-----------|-------------------|-------------------|------------------- +SetBytes | 9.09ns ±13% | 3.05ns ± 1% | -66.43% +SetBytesLE | 59.9ns ± 4% | 3.1ns ± 2% | -94.76% +Bytes | 61.3ns ± 1% | 13.8ns ± 3% | -77.49% +BytesLE | 83.5ns ± 2% | 13.9ns ± 2% | -83.32% + +### Misc Convenience Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +----------------|-------------------|-------------------|------------------- +Zero | 2.99ns ± 2% | 1.29ns ± 1% | -56.82% +IsZero | 1.78ns ± 0% | 1.63ns ± 2% | -8.23% +IsOdd | 3.62ns ± 4% | 1.64ns ± 1% | -54.65% + +### Output Formatting Methods + +Name | `big.Int` Time/Op | `Uint256` Time/Op | Delta vs `big.Int` +---------------|-------------------|-------------------|------------------- +Text/base_2 | 579ns ± 3% | 496ns ± 2% | -14.37% +Text/base_8 | 266ns ± 1% | 227ns ± 1% | -14.58% +Text/base_10 | 536ns ± 1% | 458ns ± 2% | -14.58% +Text/base_16 | 205ns ± 2% | 180ns ± 4% | -11.90% +Format/base_2 | 987ns ±15% | 852ns ± 2% | -13.64% +Format/base_8 | 620ns ± 6% | 544ns ± 3% | -12.31% +Format/base_10 | 888ns ± 1% | 726ns ± 1% | -18.25% +Format/base_16 | 565ns ± 1% | 449ns ± 1% | -20.41% + +## Installation and Updating + +This package is internal and therefore is neither directly installed nor needs +to be manually updated. + +## Examples + +* [Basic Usage](https://pkg.go.dev/github.com/decred/dcrd/internal/staging/primitives/uint256#example-package-BasicUsage) + Demonstrates calculating the result of a dividing a max unsigned 256-bit + integer by a max unsigned 128-bit integer and outputting that result in hex + with leading zeros. + +## License + +Package uint256 is licensed under the [copyfree](http://copyfree.org) ISC +License. \ No newline at end of file diff --git a/internal/staging/primitives/uint256/example_test.go b/internal/staging/primitives/uint256/example_test.go new file mode 100644 index 0000000000..edf656bf68 --- /dev/null +++ b/internal/staging/primitives/uint256/example_test.go @@ -0,0 +1,24 @@ +// Copyright (c) 2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package uint256_test + +import ( + "fmt" + + "github.com/decred/dcrd/internal/staging/primitives/uint256" +) + +// This example demonstrates calculating the result of dividing a max unsigned +// 256-bit integer by a max unsigned 128-bit integer and outputting that result +// in hex with leading zeros. +func Example_basicUsage() { + // Calculate maxUint256 / maxUint128 and output it in hex with leading zeros. + maxUint128 := new(uint256.Uint256).SetUint64(1).Lsh(128).SubUint64(1) + result := new(uint256.Uint256).Not().Div(maxUint128) + fmt.Printf("result: %064x\n", result) + + // Output: + // result: 0000000000000000000000000000000100000000000000000000000000000001 +} diff --git a/internal/staging/primitives/uint256/uint256.go b/internal/staging/primitives/uint256/uint256.go new file mode 100644 index 0000000000..c2e70ecb21 --- /dev/null +++ b/internal/staging/primitives/uint256/uint256.go @@ -0,0 +1,1820 @@ +// Copyright (c) 2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package uint256 implements highly optimized fixed precision unsigned 256-bit +// integer arithmetic. +package uint256 + +import ( + "bytes" + "fmt" + "math/big" + "math/bits" +) + +// References: +// [TAOCP2]: The Art of Computer Programming, Volume 2. +// Seminumerical Algorithms (Knuth, Donald E.) + +var ( + // zero32 is an array of 32 bytes used for the purposes of zeroing and is + // defined here to avoid extra allocations. + zero32 = [32]byte{} + + // bigUint256Mask is the value 2^256 - 1 (aka max uint256) as a stdlib big + // integer. It is defined here to save allocations in the conversion code. + bigTwo256 = new(big.Int).Lsh(big.NewInt(1), 256) + bigUint256Mask = new(big.Int).Sub(bigTwo256, big.NewInt(1)) +) + +// Uint256 implements high-performance, zero-allocation, unsigned 256-bit +// fixed-precision arithmetic. All operations are performed modulo 2^256, so +// callers may rely on "wrap around" semantics. +// +// It implements the primary arithmetic operations (addition, subtraction, +// multiplication, squaring, division, negation), bitwise operations (lsh, rsh, +// not, or, and, xor), comparison operations (equals, less, greater, cmp), +// interpreting and producing big and little endian bytes, and other convenience +// methods such as determining the minimum number of bits required to represent +// the current value, whether or not the value can be represented as a uint64 +// without loss of precision, and text formatting with base conversion. +// +// Should it be absolutely necessary, conversion to the standard library +// math/big.Int can be accomplished by using the ToBig or PutBig methods. +// However, that should typically be avoided when possible as conversion to +// big.Ints requires allocations and is slower for every operation, often to a +// significant degree. +type Uint256 struct { + // The uint256 is represented as 4 unsigned 64-bit integers in base 2^64. + // + // The following depicts the internal representation: + // + // -------------------------------------------------------------------- + // | n[3] | n[2] | n[1] | n[0] | + // | 64 bits | 64 bits | 64 bits | 64 bits | + // | Mult: 2^(64*3) | Mult: 2^(64*2) | Mult: 2^(64*1) | Mult: 2^(64*0) | + // ------------------------------------------------------------------- + // + // For example, consider the number: + // 0x0000000000000000080000000000000000000000000001000000000000000001 = + // 2^187 + 2^72 + 1 + // + // It would be represented as: + // n[0] = 1 + // n[1] = 2^8 + // n[2] = 2^59 + // n[3] = 0 + // + // The full 256-bit value is then calculated by looping i from 3..0 and + // doing sum(n[i] * 2^(64i)) as follows: + // n[3] * 2^(64*3) = 0 * 2^192 = 0 + // n[2] * 2^(64*2) = 2^59 * 2^128 = 2^187 + // n[1] * 2^(64*1) = 2^8 * 2^64 = 2^72 + // n[0] * 2^(64*0) = 1 * 2^0 = 1 + // Sum: 0 + 2^187 + 2^72 + 1 = 2^187 + 2^72 + 1 + n [4]uint64 +} + +// Set sets the uint256 equal to the same value as the passed one. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).Set(n2).AddUint64(1) so that n = n2 + 1 where n2 is not +// modified. +func (n *Uint256) Set(n2 *Uint256) *Uint256 { + *n = *n2 + return n +} + +// SetUint64 sets the uint256 to the passed unsigned 64-bit integer. This is a +// convenience function since it is fairly common to perform arithmetic with +// small native integers. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).SetUint64(2).Mul(n2) so that n = 2 * n2. +func (n *Uint256) SetUint64(n2 uint64) *Uint256 { + n.n[0] = n2 + n.n[1] = 0 + n.n[2] = 0 + n.n[3] = 0 + return n +} + +// SetBytes interprets the provided array as a 256-bit big-endian unsigned +// integer and sets the uint256 to the result. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).SetBytes(n2Bytes).AddUint64(1) so that n = n2 + 1. +func (n *Uint256) SetBytes(b *[32]byte) *Uint256 { + // Pack the 256 total bits across the 4 uint64 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant that uses a loop. + n.n[0] = uint64(b[31]) | uint64(b[30])<<8 | uint64(b[29])<<16 | + uint64(b[28])<<24 | uint64(b[27])<<32 | uint64(b[26])<<40 | + uint64(b[25])<<48 | uint64(b[24])<<56 + n.n[1] = uint64(b[23]) | uint64(b[22])<<8 | uint64(b[21])<<16 | + uint64(b[20])<<24 | uint64(b[19])<<32 | uint64(b[18])<<40 | + uint64(b[17])<<48 | uint64(b[16])<<56 + n.n[2] = uint64(b[15]) | uint64(b[14])<<8 | uint64(b[13])<<16 | + uint64(b[12])<<24 | uint64(b[11])<<32 | uint64(b[10])<<40 | + uint64(b[9])<<48 | uint64(b[8])<<56 + n.n[3] = uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | + uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | + uint64(b[1])<<48 | uint64(b[0])<<56 + return n +} + +// SetBytesLE interprets the provided array as a 256-bit little-endian unsigned +// integer and sets the uint256 to the result. +func (n *Uint256) SetBytesLE(b *[32]byte) *Uint256 { + // Pack the 256 total bits across the 4 uint64 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant that uses a loop. + n.n[0] = uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | + uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | + uint64(b[6])<<48 | uint64(b[7])<<56 + n.n[1] = uint64(b[8]) | uint64(b[9])<<8 | uint64(b[10])<<16 | + uint64(b[11])<<24 | uint64(b[12])<<32 | uint64(b[13])<<40 | + uint64(b[14])<<48 | uint64(b[15])<<56 + n.n[2] = uint64(b[16]) | uint64(b[17])<<8 | uint64(b[18])<<16 | + uint64(b[19])<<24 | uint64(b[20])<<32 | uint64(b[21])<<40 | + uint64(b[22])<<48 | uint64(b[23])<<56 + n.n[3] = uint64(b[24]) | uint64(b[25])<<8 | uint64(b[26])<<16 | + uint64(b[27])<<24 | uint64(b[28])<<32 | uint64(b[29])<<40 | + uint64(b[30])<<48 | uint64(b[31])<<56 + return n +} + +// zeroArray32 zeroes the provided 32-byte buffer. +func zeroArray32(b *[32]byte) { + copy(b[:], zero32[:]) +} + +// minInt is a helper function to return the minimum of two ints. +// This avoids a math import and the need to cast to floats. +func minInt(a, b int) int { + if a < b { + return a + } + return b +} + +// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned +// integer (meaning it is truncated to the final 32 bytes so that it is modulo +// 2^256), and sets the uint256 to the result. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).SetByteSlice(n2Slice).AddUint64(1) so that n = n2 + 1. +func (n *Uint256) SetByteSlice(b []byte) *Uint256 { + var b32 [32]byte + b = b[len(b)-minInt(len(b), 32):] + copy(b32[32-len(b):], b) + n.SetBytes(&b32) + zeroArray32(&b32) + return n +} + +// SetByteSliceLE interprets the provided slice as a 256-bit little-endian +// unsigned integer (meaning it is truncated to the first 32 bytes so that it is +// modulo 2^256), and sets the uint256 to the result. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).SetByteSliceLE(n2Slice).AddUint64(1) so that n = n2 + 1. +func (n *Uint256) SetByteSliceLE(b []byte) *Uint256 { + var b32 [32]byte + b = b[:minInt(len(b), 32)] + copy(b32[:], b) + n.SetBytesLE(&b32) + zeroArray32(&b32) + return n +} + +// PutBytesUnchecked unpacks the uint256 to a 32-byte big-endian value directly +// into the passed byte slice. The target slice must must have at least 32 +// bytes available or it will panic. +// +// There is a similar function, PutBytes, which unpacks the uint256 into a +// 32-byte array directly. This version is provided since it can be useful to +// write directly into part of a larger buffer without needing a separate +// allocation. +func (n *Uint256) PutBytesUnchecked(b []byte) { + // Unpack the 256 total bits from the 4 uint64 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant which uses a loop. + b[31] = byte(n.n[0]) + b[30] = byte(n.n[0] >> 8) + b[29] = byte(n.n[0] >> 16) + b[28] = byte(n.n[0] >> 24) + b[27] = byte(n.n[0] >> 32) + b[26] = byte(n.n[0] >> 40) + b[25] = byte(n.n[0] >> 48) + b[24] = byte(n.n[0] >> 56) + b[23] = byte(n.n[1]) + b[22] = byte(n.n[1] >> 8) + b[21] = byte(n.n[1] >> 16) + b[20] = byte(n.n[1] >> 24) + b[19] = byte(n.n[1] >> 32) + b[18] = byte(n.n[1] >> 40) + b[17] = byte(n.n[1] >> 48) + b[16] = byte(n.n[1] >> 56) + b[15] = byte(n.n[2]) + b[14] = byte(n.n[2] >> 8) + b[13] = byte(n.n[2] >> 16) + b[12] = byte(n.n[2] >> 24) + b[11] = byte(n.n[2] >> 32) + b[10] = byte(n.n[2] >> 40) + b[9] = byte(n.n[2] >> 48) + b[8] = byte(n.n[2] >> 56) + b[7] = byte(n.n[3]) + b[6] = byte(n.n[3] >> 8) + b[5] = byte(n.n[3] >> 16) + b[4] = byte(n.n[3] >> 24) + b[3] = byte(n.n[3] >> 32) + b[2] = byte(n.n[3] >> 40) + b[1] = byte(n.n[3] >> 48) + b[0] = byte(n.n[3] >> 56) +} + +// PutBytesUncheckedLE unpacks the uint256 to a 32-byte little-endian value +// directly into the passed byte slice. The target slice must must have at +// least 32 bytes available or it will panic. +// +// There is a similar function, PutBytesLE, which unpacks the uint256 into a +// 32-byte array directly. This version is provided since it can be useful to +// write directly into part of a larger buffer without needing a separate +// allocation. +func (n *Uint256) PutBytesUncheckedLE(b []byte) { + // Unpack the 256 total bits from the 4 uint64 words. This could be done + // with a for loop, but benchmarks show this unrolled version is about 2 + // times faster than the variant which uses a loop. + b[31] = byte(n.n[3] >> 56) + b[30] = byte(n.n[3] >> 48) + b[29] = byte(n.n[3] >> 40) + b[28] = byte(n.n[3] >> 32) + b[27] = byte(n.n[3] >> 24) + b[26] = byte(n.n[3] >> 16) + b[25] = byte(n.n[3] >> 8) + b[24] = byte(n.n[3]) + b[23] = byte(n.n[2] >> 56) + b[22] = byte(n.n[2] >> 48) + b[21] = byte(n.n[2] >> 40) + b[20] = byte(n.n[2] >> 32) + b[19] = byte(n.n[2] >> 24) + b[18] = byte(n.n[2] >> 16) + b[17] = byte(n.n[2] >> 8) + b[16] = byte(n.n[2]) + b[15] = byte(n.n[1] >> 56) + b[14] = byte(n.n[1] >> 48) + b[13] = byte(n.n[1] >> 40) + b[12] = byte(n.n[1] >> 32) + b[11] = byte(n.n[1] >> 24) + b[10] = byte(n.n[1] >> 16) + b[9] = byte(n.n[1] >> 8) + b[8] = byte(n.n[1]) + b[7] = byte(n.n[0] >> 56) + b[6] = byte(n.n[0] >> 48) + b[5] = byte(n.n[0] >> 40) + b[4] = byte(n.n[0] >> 32) + b[3] = byte(n.n[0] >> 24) + b[2] = byte(n.n[0] >> 16) + b[1] = byte(n.n[0] >> 8) + b[0] = byte(n.n[0]) +} + +// PutBytes unpacks the uint256 to a 32-byte big-endian value using the passed +// byte array. +// +// There is a similar function, PutBytesUnchecked, which unpacks the uint256 +// into a slice that must have at least 32 bytes available. This version is +// provided since it can be useful to write directly into an array that is type +// checked. +// +// Alternatively, there is also Bytes, which unpacks the uint256 into a new +// array and returns that which can sometimes be more ergonomic in applications +// that aren't concerned about an additional copy. +func (n *Uint256) PutBytes(b *[32]byte) { + n.PutBytesUnchecked(b[:]) +} + +// PutBytesLE unpacks the uint256 to a 32-byte little-endian value using the +// passed byte array. +// +// There is a similar function, PutBytesUncheckedLE, which unpacks the uint256 +// into a slice that must have at least 32 bytes available. This version is +// provided since it can be useful to write directly into an array that is type +// checked. +// +// Alternatively, there is also BytesLE, which unpacks the uint256 into a new +// array and returns that which can sometimes be more ergonomic in applications +// that aren't concerned about an additional copy. +func (n *Uint256) PutBytesLE(b *[32]byte) { + n.PutBytesUncheckedLE(b[:]) +} + +// Bytes unpacks the uint256 to a 32-byte big-endian array. +// +// See PutBytes and PutBytesUnchecked for variants that allow an array or slice +// to be passed which can be useful to cut down on the number of allocations +// by allowing the caller to reuse a buffer or write directly into part of a +// larger buffer. +func (n *Uint256) Bytes() [32]byte { + var b [32]byte + n.PutBytesUnchecked(b[:]) + return b +} + +// BytesLE unpacks the uint256 to a 32-byte little-endian array. +// +// See PutBytesLE and PutBytesUncheckedLE for variants that allow an array or +// slice to be passed which can be useful to cut down on the number of +// allocations by allowing the caller to reuse a buffer or write directly into +// part of a larger buffer. +func (n *Uint256) BytesLE() [32]byte { + var b [32]byte + n.PutBytesUncheckedLE(b[:]) + return b +} + +// Zero sets the uint256 to zero. A newly created uint256 is already set to +// zero. This function can be useful to clear an existing uint256 for reuse. +func (n *Uint256) Zero() { + n.n[0], n.n[1], n.n[2], n.n[3] = 0, 0, 0, 0 +} + +// IsZero returns whether or not the uint256 is equal to zero. +func (n *Uint256) IsZero() bool { + return n.n[0] == 0 && n.n[1] == 0 && n.n[2] == 0 && n.n[3] == 0 +} + +// IsOdd returns whether or not the uint256 is odd. +func (n *Uint256) IsOdd() bool { + // Only odd numbers have the bottom bit set. + return n.n[0]&1 == 1 +} + +// IsUint32 returns whether or not the uint256 can be converted to a uint32 +// without any loss of precision. In other words, 0 <= n < 2^32. +func (n *Uint256) IsUint32() bool { + return (n.n[0]>>32 | n.n[1] | n.n[2] | n.n[3]) == 0 +} + +// Uint32 returns the uint32 representation of the value. In other words, it +// returns the low-order 32 bits of the value as a uint32 which is equivalent to +// the value modulo 2^32. The caller can determine if this method can be used +// without truncation with the IsUint32 method. +func (n *Uint256) Uint32() uint32 { + return uint32(n.n[0]) +} + +// IsUint64 returns whether or not the uint256 can be converted to a uint64 +// without any loss of precision. In other words, 0 <= n < 2^64. +func (n *Uint256) IsUint64() bool { + return (n.n[1] | n.n[2] | n.n[3]) == 0 +} + +// Uint64 returns the uint64 representation of the value. In other words, it +// returns the low-order 64 bits of the value as a uint64 which is equivalent to +// the value modulo 2^64. The caller can determine if this method can be used +// without truncation with the IsUint64 method. +func (n *Uint256) Uint64() uint64 { + return n.n[0] +} + +// Eq returns whether or not the two uint256s represent the same value. +func (n *Uint256) Eq(n2 *Uint256) bool { + return n.n[0] == n2.n[0] && n.n[1] == n2.n[1] && n.n[2] == n2.n[2] && + n.n[3] == n2.n[3] +} + +// EqUint64 returns whether or not the uint256 represents the same value as the +// given uint64. +func (n *Uint256) EqUint64(n2 uint64) bool { + return n.n[0] == n2 && (n.n[1]|n.n[2]|n.n[3]) == 0 +} + +// Lt returns whether or not the uint256 is less than the given one. That is, +// it returns true when n < n2. +func (n *Uint256) Lt(n2 *Uint256) bool { + var borrow uint64 + _, borrow = bits.Sub64(n.n[0], n2.n[0], borrow) + _, borrow = bits.Sub64(n.n[1], n2.n[1], borrow) + _, borrow = bits.Sub64(n.n[2], n2.n[2], borrow) + _, borrow = bits.Sub64(n.n[3], n2.n[3], borrow) + return borrow != 0 +} + +// LtUint64 returns whether or not the uint256 is less than the given uint64. +// That is, it returns true when n < n2. +func (n *Uint256) LtUint64(n2 uint64) bool { + return n.n[0] < n2 && (n.n[1]|n.n[2]|n.n[3]) == 0 +} + +// LtEq returns whether or not the uint256 is less than or equal to the given +// one. That is, it returns true when n <= n2. +func (n *Uint256) LtEq(n2 *Uint256) bool { + return !n2.Lt(n) +} + +// LtEqUint64 returns whether or not the uint256 is less than or equal to the +// given uint64. That is, it returns true when n <= n2. +func (n *Uint256) LtEqUint64(n2 uint64) bool { + return n.n[0] <= n2 && (n.n[1]|n.n[2]|n.n[3]) == 0 +} + +// Gt returns whether or not the uint256 is greater than the given one. That +// is, it returns true when n > n2. +func (n *Uint256) Gt(n2 *Uint256) bool { + return n2.Lt(n) +} + +// GtUint64 returns whether or not the uint256 is greater than the given uint64. +// That is, it returns true when n > n2. +func (n *Uint256) GtUint64(n2 uint64) bool { + return n.n[0] > n2 || (n.n[1]|n.n[2]|n.n[3]) != 0 +} + +// GtEq returns whether or not the uint256 is greater than or equal to the given +// one. That is, it returns true when n >= n2. +func (n *Uint256) GtEq(n2 *Uint256) bool { + return !n.Lt(n2) +} + +// GtEqUint64 returns whether or not the uint256 is greater than or equal to the +// given uint64. That is, it returns true when n >= n2. +func (n *Uint256) GtEqUint64(n2 uint64) bool { + return !n.LtUint64(n2) +} + +// Cmp compares the two uint256s and returns -1, 0, or 1 depending on whether +// the uint256 is less than, equal to, or greater than the given one. +// +// That is, it returns: +// +// -1 when n < n2 +// 0 when n == n2 +// +1 when n > n2 +func (n *Uint256) Cmp(n2 *Uint256) int { + var borrow uint64 + r0, borrow := bits.Sub64(n.n[0], n2.n[0], borrow) + r1, borrow := bits.Sub64(n.n[1], n2.n[1], borrow) + r2, borrow := bits.Sub64(n.n[2], n2.n[2], borrow) + r3, borrow := bits.Sub64(n.n[3], n2.n[3], borrow) + if borrow != 0 { + return -1 + } + if r0|r1|r2|r3 == 0 { + return 0 + } + return 1 +} + +// CmpUint64 compares the uint256 with the given uint64 and returns -1, 0, or 1 +// depending on whether the uint256 is less than, equal to, or greater than the +// uint64. +// +// That is, it returns: +// +// -1 when n < n2 +// 0 when n == n2 +// +1 when n > n2 +func (n *Uint256) CmpUint64(n2 uint64) int { + if n.LtUint64(n2) { + return -1 + } + if n.GtUint64(n2) { + return 1 + } + return 0 +} + +// Add2 adds the passed two uint256s together modulo 2^256 and stores the result +// in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Add2(n1, n2).AddUint64(1) so that n = n1 + n2 + 1. +func (n *Uint256) Add2(n1, n2 *Uint256) *Uint256 { + var c uint64 + n.n[0], c = bits.Add64(n1.n[0], n2.n[0], c) + n.n[1], c = bits.Add64(n1.n[1], n2.n[1], c) + n.n[2], c = bits.Add64(n1.n[2], n2.n[2], c) + n.n[3], _ = bits.Add64(n1.n[3], n2.n[3], c) + return n +} + +// Add adds the passed uint256 to the existing one modulo 2^256 and stores the +// result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Add(n2).AddUin64(1) so that n = n + n2 + 1. +func (n *Uint256) Add(n2 *Uint256) *Uint256 { + return n.Add2(n, n2) +} + +// AddUint64 adds the passed uint64 to the existing uint256 modulo 2^256 and +// stores the result in n. +// +// The scalar is returned to support chaining. This enables syntax like: +// n.AddUint64(2).MulUint64(2) so that n = (n + 2) * 2. +func (n *Uint256) AddUint64(n2 uint64) *Uint256 { + var c uint64 + n.n[0], c = bits.Add64(n.n[0], n2, c) + n.n[1], c = bits.Add64(n.n[1], 0, c) + n.n[2], c = bits.Add64(n.n[2], 0, c) + n.n[3], _ = bits.Add64(n.n[3], 0, c) + return n +} + +// Sub2 subtracts the second given uint256 from the first modulo 2^256 and +// stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Sub2(n1, n2).AddUint64(1) so that n = n1 - n2 + 1. +func (n *Uint256) Sub2(n1, n2 *Uint256) *Uint256 { + var borrow uint64 + n.n[0], borrow = bits.Sub64(n1.n[0], n2.n[0], borrow) + n.n[1], borrow = bits.Sub64(n1.n[1], n2.n[1], borrow) + n.n[2], borrow = bits.Sub64(n1.n[2], n2.n[2], borrow) + n.n[3], _ = bits.Sub64(n1.n[3], n2.n[3], borrow) + return n +} + +// Sub subtracts the given uint256 from the existing one modulo 2^256 and stores +// the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Sub(n2).AddUint64(1) so that n = n - n2 + 1. +func (n *Uint256) Sub(n2 *Uint256) *Uint256 { + var borrow uint64 + n.n[0], borrow = bits.Sub64(n.n[0], n2.n[0], borrow) + n.n[1], borrow = bits.Sub64(n.n[1], n2.n[1], borrow) + n.n[2], borrow = bits.Sub64(n.n[2], n2.n[2], borrow) + n.n[3], _ = bits.Sub64(n.n[3], n2.n[3], borrow) + return n +} + +// SubUint64 subtracts the given uint64 from the uint256 modulo 2^256 and stores +// the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.SubUint64(1).MulUint64(3) so that n = (n - 1) * 3. +func (n *Uint256) SubUint64(n2 uint64) *Uint256 { + var borrow uint64 + n.n[0], borrow = bits.Sub64(n.n[0], n2, borrow) + n.n[1], borrow = bits.Sub64(n.n[1], 0, borrow) + n.n[2], borrow = bits.Sub64(n.n[2], 0, borrow) + n.n[3], _ = bits.Sub64(n.n[3], 0, borrow) + return n +} + +// mulAdd64 multiplies the two passed base 2^64 digits together, adds the given +// value to the result, and returns the 128-bit result via a (hi, lo) tuple +// where the upper half of the bits are returned in hi and the lower half in lo. +func mulAdd64(digit1, digit2, m uint64) (hi, lo uint64) { + // Note the carry on the final add is safe to discard because the maximum + // possible value is: + // (2^64 - 1)(2^64 - 1) + (2^64 - 1) = 2^128 - 2^64 + // and: + // 2^128 - 2^64 < 2^128. + var c uint64 + hi, lo = bits.Mul64(digit1, digit2) + lo, c = bits.Add64(lo, m, 0) + hi, _ = bits.Add64(hi, 0, c) + return hi, lo +} + +// mulAdd64Carry multiplies the two passed base 2^64 digits together, adds both +// the given value and carry to the result, and returns the 128-bit result via a +// (hi, lo) tuple where the upper half of the bits are returned in hi and the +// lower half in lo. +func mulAdd64Carry(digit1, digit2, m, c uint64) (hi, lo uint64) { + // Note the carries on the high order adds are safe to discard because the + // maximum possible value is: + // (2^64 - 1)(2^64 - 1) + 2*(2^64 - 1) = 2^128 - 1 + // and: + // 2^128 - 1 < 2^128. + var c2 uint64 + hi, lo = bits.Mul64(digit1, digit2) + lo, c2 = bits.Add64(lo, m, 0) + hi, _ = bits.Add64(hi, 0, c2) + lo, c2 = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, 0, c2) + return hi, lo +} + +// Mul2 multiplies the passed uint256s together modulo 2^256 and stores the +// result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Mul2(n1, n2).AddUint64(1) so that n = (n1 * n2) + 1. +func (n *Uint256) Mul2(n1, n2 *Uint256) *Uint256 { + // The general strategy employed here is: + // 1) Calculate the 512-bit product of the two uint256s using standard + // schoolbook multiplication. + // 2) Reduce the result modulo 2^256. + // + // However, some optimizations are used versus naively calculating all + // intermediate terms: + // 1) Reuse the high 64 bits from the intermediate 128-bit products directly + // since that is equivalent to shifting the result right by 64 bits. + // 2) Ignore all of the products between individual digits that would + // ordinarily be needed for the full 512-bit product when they are + // guaranteed to result in values that fall in the half open interval + // [2^256, 2^512) since they are all ≡ 0 (mod 2^256) given they are + // necessarily multiples of it. + // 3) Use native uint64s for the calculations involving the final digit of + // the result (r3) because all overflow carries to bits >= 256 which, as + // above, are all ≡ 0 (mod 2^256) and thus safe to discard. + var r0, r1, r2, r3, c uint64 + + // Terms resulting from the product of the first digit of the second number + // by all digits of the first number. + c, r0 = bits.Mul64(n2.n[0], n1.n[0]) + c, r1 = mulAdd64(n2.n[0], n1.n[1], c) + c, r2 = mulAdd64(n2.n[0], n1.n[2], c) + r3 = n2.n[0]*n1.n[3] + c + + // Terms resulting from the product of the second digit of the second number + // by all digits of the first number except those that are guaranteed to be + // ≡ 0 (mod 2^256). + c, r1 = mulAdd64(n2.n[1], n1.n[0], r1) + c, r2 = mulAdd64Carry(n2.n[1], n1.n[1], r2, c) + r3 += n2.n[1]*n1.n[2] + c + + // Terms resulting from the product of the third digit of the second number + // by all digits of the first number except those that are guaranteed to be + // ≡ 0 (mod 2^256). + c, r2 = mulAdd64(n2.n[2], n1.n[0], r2) + r3 += n2.n[2]*n1.n[1] + c + + // Terms resulting from the product of the fourth digit of the second number + // by all digits of the first number except those that are guaranteed to be + // ≡ 0 (mod 2^256). + r3 += n2.n[3] * n1.n[0] + + n.n[0], n.n[1], n.n[2], n.n[3] = r0, r1, r2, r3 + return n +} + +// Mul multiplies the passed uint256 by the existing value in n modulo 2^256 +// and stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Mul(n2).AddUint64(1) so that n = (n * n2) + 1. +func (n *Uint256) Mul(n2 *Uint256) *Uint256 { + return n.Mul2(n, n2) +} + +// MulUint64 multiplies the uint256 by the passed uint64 and stores the result +// in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.MulUint64(2).Add(n2) so that n = 2 * n + n2. +func (n *Uint256) MulUint64(n2 uint64) *Uint256 { + // The general strategy employed here is: + // 1) Calculate the 320-bit product of the uint256 and uint64 using standard + // schoolbook multiplication. + // 2) Reduce the result modulo 2^256. + // + // However, some optimizations are used versus naively calculating all + // intermediate terms: + // 1) Reuse the high 64 bits from the intermediate 128-bit products directly + // since that is equivalent to shifting the result right by 64 bits. + // 2) Use native uint64s for the calculations involving the final digit of + // the result because all overflow carries to bits >= 256 which is ≡ 0 + // (mod 2^256) given they are necessarily multiples of it. + var c uint64 + c, n.n[0] = bits.Mul64(n.n[0], n2) + c, n.n[1] = mulAdd64(n.n[1], n2, c) + c, n.n[2] = mulAdd64(n.n[2], n2, c) + n.n[3] = n.n[3]*n2 + c + return n +} + +// SquareVal squares the passed uint256 modulo 2^256 and stores the result in +// n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.SquareVal(n2).Mul(n2) so that n = n2^2 * n2 = n2^3. +func (n *Uint256) SquareVal(n2 *Uint256) *Uint256 { + // Similar to multiplication, the general strategy employed here is: + // 1) Calculate the 512-bit product of the two uint256s using standard + // schoolbook multiplication. + // 2) Reduce the result modulo 2^256. + // + // However, some optimizations are used versus naively calculating all + // intermediate terms: + // 1) Reuse the high 64 bits from the intermediate 128-bit products directly + // since that is equivalent to shifting the result right by 64 bits. + // 2) Ignore all of the products between individual digits that would + // ordinarily be needed for the full 512-bit product when they are + // guaranteed to result in values that fall in the half open interval + // [2^256, 2^512) since they are all ≡ 0 (mod 2^256) given they are + // necessarily multiples of it. + // 3) Use native uint64s for the calculations involving the final digit of + // the result (r3) because all overflow carries to bits >= 256 which, as + // above, are all ≡ 0 (mod 2^256) and thus safe to discard. + // 4) Use the fact the number is being multiplied by itself to take + // advantage of symmetry to double the result of some individual products + // versus calculating them again. + var r0, r1, r2, r3, c uint64 + + // Terms resulting from the product of the first digit of the second number + // by all digits of the first number except those that will be accounted for + // later via symmetry. + c, r0 = bits.Mul64(n2.n[0], n2.n[0]) + c, r1 = mulAdd64(n2.n[0], n2.n[1], c) + c, r2 = mulAdd64(n2.n[0], n2.n[2], c) + r3 = c + + // Terms resulting from the product of the second digit of the second number + // by all digits of the first number except those that will be accounted for + // later via symmetry and those that are guaranteed to be ≡ 0 (mod 2^256). + c, r1 = mulAdd64(n2.n[1], n2.n[0], r1) + c, r2 = mulAdd64Carry(n2.n[1], n2.n[1], r2, c) + r3 += c + + // Terms resulting from the product of the third digit of the second number + // by all digits of the first number except those that will be accounted for + // later via symmetry and those that are guaranteed to be ≡ 0 (mod 2^256). + c, r2 = mulAdd64(n2.n[2], n2.n[0], r2) + r3 += c + + // Terms resulting from the product of the fourth digit of the second number + // by all digits of the first number except those that are guaranteed to be + // ≡ 0 (mod 2^256) and doubling those that were skipped earlier. + r3 += 2 * (n2.n[0]*n2.n[3] + n2.n[1]*n2.n[2]) + + n.n[0], n.n[1], n.n[2], n.n[3] = r0, r1, r2, r3 + return n +} + +// Square squares the uint256 modulo 2^256 and stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Square().Mul(n2) so that n = n^2 * n2. +func (n *Uint256) Square() *Uint256 { + return n.SquareVal(n) +} + +// numDigits returns the number of base 2^64 digits required to represent the +// uint256. The result is 0 when the value is 0. +func (n *Uint256) numDigits() int { + for i := 3; i >= 0; i-- { + if n.n[i] != 0 { + return i + 1 + } + } + return 0 +} + +// prefixLt returns whether or not the first argument is less than the prefix of +// the same length from the second when treating both as little-endian encoded +// integers. That is, it returns true when a < b[0:len(a)]. +func prefixLt(a, b []uint64) bool { + var borrow uint64 + for i := 0; i < len(a); i++ { + _, borrow = bits.Sub64(a[i], b[i], borrow) + } + return borrow != 0 +} + +// Div2 divides the passed uint256 dividend by the passed uint256 divisor modulo +// 2^256 and stores the result in n. It will panic if the divisor is 0. +// +// This implements truncated division like native Go integers and it is safe to +// alias the arguments. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Div2(n1, n2).AddUint64(1) so that n = (n1 / n2) + 1. +func (n *Uint256) Div2(dividend, divisor *Uint256) *Uint256 { + if divisor.IsZero() { + panic("division by zero") + } + + // Fast path for a couple of obvious cases. The result is zero when the + // divisor is larger than the dividend and one when they are equal. The + // remaining code also has preconditions on these cases already being + // handled. + if divisor.Gt(dividend) { + n.Zero() + return n + } + if dividend.Eq(divisor) { + return n.SetUint64(1) + } + + // When the dividend can be fully represented by a uint64, perform the + // division using native uint64s since the divisor is also guaranteed to be + // fully representable as a uint64 given it was already confirmed to be + // smaller than the dividend above. + if dividend.IsUint64() { + return n.SetUint64(dividend.n[0] / divisor.n[0]) + } + + // When the divisor can be fully represented by a uint64, the divisor only + // consists of a single base 2^64 digit, so use that fact to avoid extra + // work. It is important to note that the algorithm below also requires the + // divisor to be at least two digits, so this is not solely a performance + // optimization. + if divisor.IsUint64() { + // The range here satisfies the following inequality: + // 1 ≤ divisor < 2^64 ≤ dividend < 2^256 + var quotient Uint256 + var r uint64 + for d := dividend.numDigits() - 1; d >= 0; d-- { + quotient.n[d], r = bits.Div64(r, dividend.n[d], divisor.n[0]) + } + return n.Set("ient) + } + + // The range at this point satisfies the following inequality: + // 2^64 ≤ divisor < dividend < 2^256 + + // ------------------------------------------------------------------------- + // The following is loosely based on Algorithm 4.3.1D of [TAOCP2] and is + // therefore based on long (aka schoolbook) division. It has been modified + // as compared to said algorithm in the following ways for performance + // reasons: + // + // 1. It uses full words instead of splitting each one into half words + // 2. It does not perform the test which effectively expands the estimate to + // be based on 3 digits of the dividend/remainder divided by 2 digits + // of the divisor instead of 2 digits by 1 (Step D3) + // 3. It conditionally subtracts the partial product from the partial + // remainder in the case the latter is less than the former instead of + // always subtracting it and then conditionally adding back (Steps D4, + // D5, and D6) + // 4. It only computes the active part of the remainder and throws the rest + // away since it is not needed here (parts of Steps D3, D4, D5, and D6) + // 5. It skips undoing the normalization for the remainder since it is not + // needed here (Step D8) + // + // The primary rationale for these changes follows: + // + // In regards to (1), almost all modern hardware provides native 128-bit by + // 64-bit division operations as well as 64-bit by 64-bit multiplication + // operations which the Go compiler uses in place of the respective + // `bits.Div64/Mul64` and, as a further benefit, using full words cuts the + // number of iterations in half. + // + // The test referred to by (2) serves to pin the potential error in the + // estimated quotient to be a max of 1 too high instead of a max of 2 as + // well as reduce the probability that single correction is needed at all. + // However, in order to achieve that, given full 64-bit words are used here, + // the Knuth method would involve an unconditional test which relies on + // comparing the result of a software-implemented 128-bit product against + // another 128-bit product as well as a 128-bit addition with carry. That + // is typically a sensible tradeoff when the number of digits involved is + // large and when working with half words since an additional potential + // correction would typically cost more than the test under those + // circumstances. + // + // However, in this implementation, it ends being faster to perform the + // extra correction than it is to perform the aforementioned test. The + // primary reasons for this are: + // + // - The number of digits is ≤ 4 which means very few operations are needed + // for the correction + // - Both the comparison of the product against the remainder and the + // conditional subtraction of the product from the remainder are able to + // make use of hardware acceleration that almost all modern hardware + // provides + // + // Moreover, the probability of the initial estimate being correct + // significantly increases as the size of the base grows. Given this + // implementation uses a base of 2^64, the probability of needing a + // correction is extremely low. + // + // Concretely, for quotient digits that are uniformly distributed, the + // probability of guessing the initial estimate correctly with normalization + // for a base of 2^64 is: 1 - 2/2^64 ~= 99.99999999999999999%. In other + // words, on average, roughly only 1 out of every 10 quintillion (10^19) + // will require an adjustment. + // ------------------------------------------------------------------------- + + // Start by normalizing the arguments. + // + // For reference, normalization is the process of scaling the operands such + // that the leading digit of the divisor is greater than or equal to half of + // the base (also often called the radix). Since this implementation uses a + // base of 2^64, half of that equates to 2^63. Mathematically: + // 2^63 ≤ leading digit of divisor < 2^64 + // + // Note that the number of digits in each operand at this point satisfies + // the following inequality: + // 2 ≤ num divisor digits ≤ num dividend digits ≤ 4 + // + // Further, notice that being ≥ 2^63 is equivalent to a digit having its + // most significant bit set. This means the scaling factor can very quickly + // be determined by counting the number of leading zeros and applied to the + // operands via bit shifts. + // + // This process significantly reduces the probability of requiring an + // adjustment to the initial estimate for quotient digits later as per the + // previous comments as well as bounds the possible number of error + // adjustments. + numDivisorDigits := divisor.numDigits() + numDividendDigits := dividend.numDigits() + sf := uint8(bits.LeadingZeros64(divisor.n[numDivisorDigits-1])) + var divisorN [4]uint64 + var dividendN [5]uint64 + if sf > 0 { + // Normalize the divisor by scaling it per the scaling factor. + for i := numDivisorDigits - 1; i > 0; i-- { + divisorN[i] = divisor.n[i]<>(64-sf) + } + divisorN[0] = divisor.n[0] << sf + + // Scale the dividend by the same scaling factor too. + dividendN[numDividendDigits] = dividend.n[numDividendDigits-1] >> (64 - sf) + for i := numDividendDigits - 1; i > 0; i-- { + dividendN[i] = dividend.n[i]<>(64-sf) + } + dividendN[0] = dividend.n[0] << sf + } else { + copy(divisorN[:], divisor.n[:]) + copy(dividendN[:], dividend.n[:]) + } + + // NOTE: The number of digits in the non-normalized dividend and divisor + // satisfies the following inequality at this point: + // 2 ≤ numDivisorDigits ≤ numDividendDigits ≤ 4 + // + // Therefore the maximum value of d in the loop is 2. + var p [5]uint64 + var qhat, c, borrow uint64 + n.Zero() + for d := numDividendDigits - numDivisorDigits; d >= 0; d-- { + // Estimate the quotient digit by dividing the 2 leading digits of the + // active part of the remainder by the leading digit of the normalized + // divisor. + // + // Per Theorems A and B in section 4.3.1 of [TAOCP2], when the leading + // digit of the divisor is normalized as described above, the estimated + // quotient digit (qhat) produced by this is guaranteed to be ≥ the + // correct one (q) and at most 2 more. Mathematically: + // qhat - 2 ≤ q ≤ qhat + // + // Further, the algorithm implemented here guarantees that the leading + // digit of the active part of the remainder will be ≤ the leading digit + // of the normalized divisor. When it is less, the estimated quotient + // digit will fit into a single word and everything is normal. However, + // in the equals case, a second word would be required meaning the + // calculation would overflow. + // + // Combining that with the theorem above, the estimate in the case of + // overflow must either be one or two more than the maximum possible + // value for a single digit. In either case, assuming a type capable of + // representing values larger than a single digit without overflow were + // used instead, the quotient digit would have to be adjusted downwards + // until it was correct, and given the correct quotient digit must fit + // into a single word, it will necessarily either be the maximum + // possible value allowed for a single digit or one less. Therefore, + // the equality check here bypasses the overflow by setting the estimate + // to the max possible value and allowing the loop below to handle the + // potential remaining (extremely rare) overestimate. + // + // Also note that this behavior is not merely an optimization to skip + // the 128-bit by 64-bit division as both the hardware instruction and + // the bits.Div64 implementation would otherwise lead to a panic due to + // overflow as well. + // + // For some intuition, consider the base 10 case with a divisor of 5 + // (recall the divisor is normalized, so it must be at least half the + // base). Then the maximum 2-digit dividend with the leading digit less + // than 5 is 49 and the minimum with the leading digit equal to 5 is 50. + // Observe that floor(49/5) = 9 is a single digit result, but + // floor(50/5) = 10 is not. The worst overestimate case for these + // conditions would occur at floor(59/5) = floor(11.8) = 11 which is + // indeed 2 more than the maximum possible single digit 9. + if dividendN[d+numDivisorDigits] == divisorN[numDivisorDigits-1] { + qhat = ^uint64(0) + } else { + qhat, _ = bits.Div64(dividendN[d+numDivisorDigits], + dividendN[d+numDivisorDigits-1], divisorN[numDivisorDigits-1]) + } + + // Calculate the product of the estimated quotient digit and divisor. + // + // This is semantically equivalent to the following if uint320 were + // supported: + // + // p = uint320(qhat) * uint320(divisorN) + // + // Note that this will compute extra upper terms when the divisor is + // fewer digits than the max possible even though it is not really + // needed since they will all be zero. However, because it is only + // potentially a max of two extra digits, it ends up being faster on + // average to avoid the additional conditional branches due to + // pipelining. + c, p[0] = bits.Mul64(qhat, divisorN[0]) + c, p[1] = mulAdd64(qhat, divisorN[1], c) + c, p[2] = mulAdd64(qhat, divisorN[2], c) + p[4], p[3] = mulAdd64(qhat, divisorN[3], c) + + // Adjust the estimate (and associated product) downwards when they are + // too high for the active part of the partial remainder. As described + // above, qhat is guaranteed to be at most two more than the correct + // quotient digit, so this loop will run at most twice. + // + // Moreover, the probability of a single correction is extremely rare + // and that of two corrections is roughly an additional two orders of + // magnitude less than that. In other words, in practice, the two + // corrections case almost never happens. + for prefixLt(dividendN[d:d+numDivisorDigits+1], p[:]) { + qhat-- + + // Subtract the divisor from the product to adjust it for the + // reduced quotient. + // + // This is semantically equivalent to the following if uint320 were + // supported (and with p as a uint320 per above): + // + // p -= uint320(divisorN) + // + // Note that as with the original calculation of the product above, + // this will compute extra upper terms when the divisor is fewer + // digits than the max possible even though it is not really needed + // for performance reasons as previously described. + p[0], borrow = bits.Sub64(p[0], divisorN[0], 0) + p[1], borrow = bits.Sub64(p[1], divisorN[1], borrow) + p[2], borrow = bits.Sub64(p[2], divisorN[2], borrow) + p[3], borrow = bits.Sub64(p[3], divisorN[3], borrow) + p[4] -= borrow + } + + // Set the quotient digit in the result. + n.n[d] = qhat + + // Update the dividend by subtracting the resulting product from it so + // that it becomes the new remainder to use for calculating the next + // quotient digit. + // + // Note that this is only calculating the _active_ part of the remainder + // since the final remainder is not needed and the next iteration slides + // over one digit to the right meaning none of the upper digits are used + // and are therefore safe to ignore despite them no longer being + // accurate. + // + // Also, since 'd' is a maximum of 2 due to previous constraints, there + // is no chance of overflowing the array. + // + // This is semantically equivalent to the following if uint192 and the + // syntax to set base 2^64 digits in little endian directly were + // supported: + // + // dividendN[d:d+3] = uint192(dividendN[d:d+3]) - uint192(p[0:3]) + dividendN[d], borrow = bits.Sub64(dividendN[d], p[0], 0) + dividendN[d+1], borrow = bits.Sub64(dividendN[d+1], p[1], borrow) + dividendN[d+2], _ = bits.Sub64(dividendN[d+2], p[2], borrow) + } + + return n +} + +// Div divides the existing value in n by the passed uint256 divisor modulo +// 2^256 and stores the result in n. It will panic if the divisor is 0. +// +// This implements truncated division like native Go integers. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Div(n2).AddUint64(1) so that n = (n / n2) + 1. +func (n *Uint256) Div(divisor *Uint256) *Uint256 { + return n.Div2(n, divisor) +} + +// DivUint64 divides the existing value in n by the passed uint64 divisor modulo +// 2^256 and stores the result in n. It will panic if the divisor is 0. +// +// This implements truncated division like native Go integers. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.DivUint64(2).AddUint64(1) so that n = (n / 2) + 1. +func (n *Uint256) DivUint64(divisor uint64) *Uint256 { + if divisor == 0 { + panic("division by zero") + } + + // Fast path for a couple of obvious cases. The result is zero when the + // dividend is smaller than the divisor and one when they are equal. The + // remaining code also has preconditions on these cases already being + // handled. + if n.LtUint64(divisor) { + n.Zero() + return n + } + if n.EqUint64(divisor) { + return n.SetUint64(1) + } + + // The range here satisfies the following inequalities: + // 1 ≤ divisor < 2^64 + // 1 ≤ divisor < dividend < 2^256 + var quotient Uint256 + var r uint64 + for d := n.numDigits() - 1; d >= 0; d-- { + quotient.n[d], r = bits.Div64(r, n.n[d], divisor) + } + return n.Set("ient) +} + +// NegateVal negates the passed uint256 modulo 2^256 and stores the result in +// n. In other words, n will be set to the two's complement of the passed +// uint256. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.NegateVal(n2).AddUint64(1) so that n = -n2 + 1. +func (n *Uint256) NegateVal(n2 *Uint256) *Uint256 { + // This uses math/bits to perform the negation via subtraction from 0 as the + // compiler will replace it with the relevant intrinsic on most + // architectures. + var borrow uint64 + n.n[0], borrow = bits.Sub64(0, n2.n[0], borrow) + n.n[1], borrow = bits.Sub64(0, n2.n[1], borrow) + n.n[2], borrow = bits.Sub64(0, n2.n[2], borrow) + n.n[3], _ = bits.Sub64(0, n2.n[3], borrow) + return n +} + +// Negate negates the uint256 modulo 2^256. In other words, n will be set to +// its two's complement. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Negate().AddUint64(1) so that n = -n + 1. +func (n *Uint256) Negate() *Uint256 { + return n.NegateVal(n) +} + +// LshVal shifts the passed uint256 to the left the given number of bits and +// stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.LshVal(n2, 2).AddUint64(1) so that n = (n2 << 2) + 1. +func (n *Uint256) LshVal(n2 *Uint256, bits uint32) *Uint256 { + // Fast path for large and zero shifts. + if bits > 255 { + n.Zero() + return n + } + if bits == 0 { + return n.Set(n2) + } + + // Shift entire words when possible. + switch { + case bits >= 192: + // Left shift 192 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = n2.n[0], 0, 0, 0 + bits -= 192 + if bits == 0 { + return n + } + n.n[3] <<= bits + return n + + case bits >= 128: + // Left shift 128 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = n2.n[1], n2.n[0], 0, 0 + bits -= 128 + if bits == 0 { + return n + } + n.n[3] = (n.n[3] << bits) | (n.n[2] >> (64 - bits)) + n.n[2] <<= bits + return n + + case bits >= 64: + // Left shift 64 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = n2.n[2], n2.n[1], n2.n[0], 0 + bits -= 64 + if bits == 0 { + return n + } + n.n[3] = (n.n[3] << bits) | (n.n[2] >> (64 - bits)) + n.n[2] = (n.n[2] << bits) | (n.n[1] >> (64 - bits)) + n.n[1] <<= bits + return n + } + + // At this point the shift must be less than 64 bits, so shift each word + // accordingly. + n.n[3] = (n2.n[3] << bits) | (n2.n[2] >> (64 - bits)) + n.n[2] = (n2.n[2] << bits) | (n2.n[1] >> (64 - bits)) + n.n[1] = (n2.n[1] << bits) | (n2.n[0] >> (64 - bits)) + n.n[0] = n2.n[0] << bits + return n +} + +// Lsh shifts the uint256 to the left the given number of bits and stores the +// result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Lsh(2).AddUint64(1) so that n = (n << 2) + 1. +func (n *Uint256) Lsh(bits uint32) *Uint256 { + if bits == 0 { + return n + } + return n.LshVal(n, bits) +} + +// RshVal shifts the passed uint256 to the right the given number of bits and +// stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.RshVal(n2, 2).AddUint64(1) so that n = (n2 >> 2) + 1. +func (n *Uint256) RshVal(n2 *Uint256, bits uint32) *Uint256 { + // Fast path for large and zero shifts. + if bits > 255 { + n.Zero() + return n + } + if bits == 0 { + return n.Set(n2) + } + + // Shift entire words when possible. + switch { + case bits >= 192: + // Right shift 192 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = 0, 0, 0, n2.n[3] + bits -= 192 + if bits == 0 { + return n + } + n.n[0] >>= bits + return n + + case bits >= 128: + // Right shift 128 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = 0, 0, n2.n[3], n2.n[2] + bits -= 128 + if bits == 0 { + return n + } + n.n[0] = (n.n[0] >> bits) | (n.n[1] << (64 - bits)) + n.n[1] >>= bits + return n + + case bits >= 64: + // Right shift 64 bits. + n.n[3], n.n[2], n.n[1], n.n[0] = 0, n2.n[3], n2.n[2], n2.n[1] + bits -= 64 + if bits == 0 { + return n + } + n.n[0] = (n.n[0] >> bits) | (n.n[1] << (64 - bits)) + n.n[1] = (n.n[1] >> bits) | (n.n[2] << (64 - bits)) + n.n[2] >>= bits + return n + } + + // At this point the shift must be less than 64 bits, so shift each word + // accordingly. + n.n[0] = (n2.n[0] >> bits) | (n2.n[1] << (64 - bits)) + n.n[1] = (n2.n[1] >> bits) | (n2.n[2] << (64 - bits)) + n.n[2] = (n2.n[2] >> bits) | (n2.n[3] << (64 - bits)) + n.n[3] = n2.n[3] >> bits + return n +} + +// Rsh shifts the uint256 to the right the given number of bits and stores the +// result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Rsh(2).AddUint64(1) so that n = (n >> 2) + 1. +func (n *Uint256) Rsh(bits uint32) *Uint256 { + if bits == 0 { + return n + } + return n.RshVal(n, bits) +} + +// Not computes the bitwise not of the uint256 and stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Not().AddUint64(1) so that n = ^n + 1. +func (n *Uint256) Not() *Uint256 { + n.n[0] = ^n.n[0] + n.n[1] = ^n.n[1] + n.n[2] = ^n.n[2] + n.n[3] = ^n.n[3] + return n +} + +// Or computes the bitwise or of the uint256 and the passed uint256 and stores +// the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Or(n2).AddUint64(1) so that n = (n | n2) + 1. +func (n *Uint256) Or(n2 *Uint256) *Uint256 { + n.n[0] |= n2.n[0] + n.n[1] |= n2.n[1] + n.n[2] |= n2.n[2] + n.n[3] |= n2.n[3] + return n +} + +// And computes the bitwise and of the uint256 and the passed uint256 and stores +// the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.And(n2).AddUint64(1) so that n = (n & n2) + 1. +func (n *Uint256) And(n2 *Uint256) *Uint256 { + n.n[0] &= n2.n[0] + n.n[1] &= n2.n[1] + n.n[2] &= n2.n[2] + n.n[3] &= n2.n[3] + return n +} + +// Xor computes the bitwise exclusive or of the uint256 and the passed uint256 +// and stores the result in n. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n.Xor(n2).AddUint64(1) so that n = (n ^ n2) + 1. +func (n *Uint256) Xor(n2 *Uint256) *Uint256 { + n.n[0] ^= n2.n[0] + n.n[1] ^= n2.n[1] + n.n[2] ^= n2.n[2] + n.n[3] ^= n2.n[3] + return n +} + +// BitLen returns the minimum number of bits required to represent the uint256. +// The result is 0 when the value is 0. +func (n *Uint256) BitLen() uint16 { + if w := n.n[3]; w > 0 { + return uint16(bits.Len64(w)) + 192 + } + if w := n.n[2]; w > 0 { + return uint16(bits.Len64(w)) + 128 + } + if w := n.n[1]; w > 0 { + return uint16(bits.Len64(w)) + 64 + } + return uint16(bits.Len64(n.n[0])) +} + +// bitsPerInternalWord is the number of bits used for each internal word of the +// uint256. +const bitsPerInternalWord = 64 + +// toBin converts the uint256 to its string representation in base 2. +func (n *Uint256) toBin() []byte { + if n.IsZero() { + return []byte("0") + } + + // Create space for the max possible number of output digits. + maxOutDigits := n.BitLen() + result := make([]byte, maxOutDigits) + + // Convert each internal base 2^64 word to base 2 from least to most + // significant. Since the value is guaranteed to be non-zero per a previous + // check, there will always be a nonzero most-significant word. Also, note + // that no partial digit handling is needed in this case because the shift + // amount evenly divides the bits per internal word. + const shift = 1 + const mask = 1<>= shift + outputIdx-- + } + inputWord = n.n[inputIdx] + } + for inputWord != 0 { + result[outputIdx] = '0' + byte(inputWord&mask) + inputWord >>= shift + outputIdx-- + } + + return result[outputIdx+1:] +} + +// toOctal converts the uint256 to its string representation in base 8. +func (n *Uint256) toOctal() []byte { + if n.IsZero() { + return []byte("0") + } + + // Create space for the max possible number of output digits using the fact + // that 3 bits converts directly to a single octal digit. + maxOutDigits := (n.BitLen() + 2) / 3 + result := make([]byte, maxOutDigits) + + // Convert each internal base 2^64 word to base 8 from least to most + // significant. Since the value is guaranteed to be non-zero per a previous + // check, there will always be a nonzero most-significant word. Also, note + // that partial digit handling is needed in this case because the shift + // amount does not evenly divide the bits per internal word. + const shift = 3 + const mask = 1<= shift; unconvertedBits -= shift { + result[outputIdx] = '0' + byte(inputWord&mask) + inputWord >>= shift + outputIdx-- + } + + // Move to the next input word when there are not any remaining + // unconverted bits that need to be handled. + if unconvertedBits == 0 { + inputWord = n.n[inputIdx] + unconvertedBits = bitsPerInternalWord + continue + } + + // Account for the remaining unconverted bits from the current word and + // the bits needed from the next word to form a full digit for the next + // digit. + inputWord |= n.n[inputIdx] << unconvertedBits + result[outputIdx] = '0' + byte(inputWord&mask) + outputIdx-- + + // Move to the next input word while accounting for the bits already + // consumed above by shifting it and updating the unconverted bits + // accordingly. + inputWord = n.n[inputIdx] >> (shift - unconvertedBits) + unconvertedBits = bitsPerInternalWord - (shift - unconvertedBits) + } + for inputWord != 0 { + result[outputIdx] = '0' + byte(inputWord&mask) + inputWord >>= shift + outputIdx-- + } + + return result[outputIdx+1:] +} + +// maxPow10ForInternalWord is the maximum power of 10 that will fit into an +// internal word. It is the value 10^floor(64 / log2(10)) and is used when +// converting to base 10 in order to significantly reduce the number of +// divisions needed. +var maxPow10ForInternalWord = new(Uint256).SetUint64(1e19) + +// toDecimal converts the uint256 to its string representation in base 10. +func (n *Uint256) toDecimal() []byte { + if n.IsZero() { + return []byte("0") + } + + // Create space for the max possible number of output digits. + // + // Note that the actual total number of output digits is usually calculated + // as: + // floor(log2(n) / log2(base)) + 1 + // + // However, in order to avoid more expensive calculation of the full log2 of + // the value, the code below instead calculates a value that might overcount + // by a max of one digit and trims the result as needed via the following + // slightly modified version of the formula: + // floor(bitlen(n) / log2(base)) + 1 + // + // The modified formula is guaranteed to be large enough because: + // (a) floor(log2(x)) ≤ log2(x) ≤ floor(log2(x)) + 1 + // (b) bitlen(x) = floor(log2(x)) + 1 + // + // Which implies: + // (c) floor(log2(n) / log2(base)) ≤ floor(floor(log2(n))+1) / log2(base)) + // (d) floor(log2(n) / log2(base)) ≤ floor(bitlen(n)) / log2(base)) + // + // Note that (c) holds since the left hand side of the inequality has a + // dividend that is ≤ the right hand side dividend due to (a) while the + // divisor is = the right hand side divisor, and then (d) is equal to (c) + // per (b). Adding 1 to both sides of (d) yields an inequality where the + // left hand side is the typical formula and the right hand side is the + // modified formula thereby proving it will never under count. + const log2Of10 = 3.321928094887362 + maxOutDigits := uint8(float64(n.BitLen())/log2Of10) + 1 + result := make([]byte, maxOutDigits) + + // Convert each internal base 2^64 word to base 10 from least to most + // significant. Since the value is guaranteed to be non-zero per a previous + // check, there will always be a nonzero most-significant word. Also, note + // that partial digit handling is needed in this case because the shift + // amount does not evenly divide the bits per internal word. + var quo, rem, t Uint256 + var r uint64 + outputIdx := maxOutDigits - 1 + quo = *n + for !quo.IsZero() { + rem.Set(&quo) + quo.Div(maxPow10ForInternalWord) + t.Mul2(&quo, maxPow10ForInternalWord) + inputWord := rem.Sub(&t).Uint64() + for inputWord != 0 { + inputWord, r = inputWord/10, inputWord%10 + result[outputIdx] = '0' + byte(r) + outputIdx-- + } + } + + return result[outputIdx+1:] +} + +// toHex converts the uint256 to its string representation in lowercase base 16. +func (n *Uint256) toHex() []byte { + if n.IsZero() { + return []byte("0") + } + + // Create space for the max possible number of output digits using the fact + // that a nibble converts directly to a single hex digit. + maxOutDigits := (n.BitLen() + 3) / 4 + result := make([]byte, maxOutDigits) + + // Convert each internal base 2^64 word to base 16 from least to most + // significant. Since the value is guaranteed to be non-zero per a + // previous check, there will always be a nonzero most-significant word. + // Also, note that no partial digit handling is needed in this case + // because the shift amount evenly divides the bits per internal word. + const alphabet = "0123456789abcdef" + const shift = 4 + const mask = 1<>= shift + outputIdx-- + } + inputWord = n.n[inputIdx] + } + for inputWord != 0 { + result[outputIdx] = alphabet[inputWord&mask] + inputWord >>= shift + outputIdx-- + } + + return result[outputIdx+1:] +} + +// OutputBase represents a specific base to use for the string representation of +// a number. +type OutputBase int + +// These constants define the supported output bases. +const ( + // OutputBaseBinary indicates a string representation of a uint256 in + // base 2. + OutputBaseBinary OutputBase = 2 + + // OutputBaseOctal indicates a string representation of a uint256 in base 8. + OutputBaseOctal OutputBase = 8 + + // OutputBaseDecimal indicates a string representation of a uint256 in base + // 10. + OutputBaseDecimal OutputBase = 10 + + // OutputBaseHex indicates a string representation of a uint256 in base 16. + OutputBaseHex OutputBase = 16 +) + +// Text returns the string representation of the uint256 in the given base which +// must be on of the supported bases as defined by the OutputBase type. +// +// It will return "" when the uint256 pointer is nil and a message that +// indicates the base is not supported along with the value in base 10 in the +// case the caller goes out of its way to call it with an invalid base. +func (n *Uint256) Text(base OutputBase) string { + if n == nil { + return "" + } + + switch base { + case OutputBaseHex: + return string(n.toHex()) + case OutputBaseDecimal: + return string(n.toDecimal()) + case OutputBaseBinary: + return string(n.toBin()) + case OutputBaseOctal: + return string(n.toOctal()) + } + + return fmt.Sprintf("base %d not supported (Uint256=%s)", int(base), n) +} + +// String returns the scalar as a human-readable decimal string. +func (n Uint256) String() string { + return string(n.toDecimal()) +} + +// Format implements fmt.Formatter. It accepts the following format verbs: +// +// 'v' default format which is decimal +// 's' default string format which is decimal +// 'b' binary +// 'o' octal with 0 prefix when accompanied by # +// 'O' octal with 0o prefix +// 'd' decimal +// 'x' lowercase hexadecimal +// 'X' uppercase hexadecimal +// +// It also supports the full suite of the fmt package format flags for integral +// types: +// +// '#' output base prefix: +// binary: 0b (%#b) +// octal: 0 (%#o) +// hex: 0x (%#x) or 0X (%#X) +// '-' pad with spaces on the right (left-justify field) +// '0' pad with leading zeros rather than spaces +// +// Finally, it supports specification of the minimum number of digits +// (precision) and output field width. Examples: +// +// %#.64x default width, precision 64, lowercase hex with 0x prefix +// %256b width 256, default precision, binary with leading zeros +// %12.3O width 12, precision 3, octal with 0o prefix +func (n Uint256) Format(s fmt.State, ch rune) { + // Determine output digits for the output base. + var digits []byte + switch ch { + case 'b': + digits = n.toBin() + case 'o', 'O': + digits = n.toOctal() + case 'd', 's', 'v': + digits = n.toDecimal() + case 'x': + digits = n.toHex() + case 'X': + digits = n.toHex() + for i, d := range digits { + if d >= 'a' && d <= 'f' { + digits[i] = 'A' + (d - 'a') + } + } + + default: + fmt.Fprintf(s, "%%!%c(Uint256=%s)", ch, n.String()) + return + } + + // Determine prefix characters for the output base as needed. + var prefix string + if s.Flag('#') { + switch ch { + case 'b': + prefix = "0b" + case 'o': + prefix = "0" + case 'x': + prefix = "0x" + case 'X': + prefix = "0X" + } + } + if ch == 'O' { + prefix = "0o" + } + + // Determine how many zeros to pad with based on whether or not a minimum + // number of digits to output is specified. + // + // Also, do not output anything when the minimum number of digits to output + // is zero and the uint256 is zero. + // + // Note that the zero padding might also be set below when the zero pad + // ('0') flag is specified and neither a precision nor the right justify + // ('-') flag is specified. + var zeroPad int + minDigits, isPrecisionSet := s.Precision() + if isPrecisionSet { + switch { + case len(digits) < minDigits: + zeroPad = minDigits - len(digits) + case minDigits == 0 && n.IsZero(): + return + } + } + + // Determine the left or right padding depending on whether or not a minimum + // number of characters to output is specified as well as the flags. + // + // A '-' flag indicates the output should be right justified and takes + // precedence over the zero pad ('0') flag. Per the above, the zero pad + // flag is ignored when a minimum number of digits is specified. + var leftPad, rightPad int + digitsPlusPrefixLen := len(prefix) + zeroPad + len(digits) + width, isWidthSet := s.Width() + if isWidthSet && digitsPlusPrefixLen < width { + switch pad := width - digitsPlusPrefixLen; { + case s.Flag('-'): + rightPad = pad + case s.Flag('0') && !isPrecisionSet: + zeroPad = pad + default: + leftPad = pad + } + } + + // Produce the following output: + // + // [left pad][prefix][zero pad][digits][right pad] + var buf bytes.Buffer + buf.Grow(leftPad + len(prefix) + zeroPad + len(digits) + rightPad) + for i := 0; i < leftPad; i++ { + buf.WriteRune(' ') + } + buf.WriteString(prefix) + for i := 0; i < zeroPad; i++ { + buf.WriteRune('0') + } + buf.Write(digits) + for i := 0; i < rightPad; i++ { + buf.WriteRune(' ') + } + s.Write(buf.Bytes()) +} + +// PutBig sets the passed existing stdlib big integer to the value the uint256 +// currently represents. +// +// This can sometimes be useful to reduce the number of allocations due to +// conversion if reusing the same variable is an option. The reason is that +// stdlib big integers internally allocate space on the heap to perform their +// operations and attempt to reuse that internal buffer when possible. +// +// Do note however that even when reusing a big integer, it will naturally still +// require an allocation for the internal buffer unless it has already allocated +// one large enough to be reused. Moreover, they often require further +// allocations while performing arithmetic, notably multiplication and division. +// +// Applications that are performance sensitive should consider avoiding +// conversion to big integers altogether when possible. +// +// See ToBig for a variant that returns the uint256 as a new stdlib big integer +// instead which can sometimes be more ergonomic in contexts where additional +// allocations are not a concern. +func (n *Uint256) PutBig(out *big.Int) { + b := n.Bytes() + out.SetBytes(b[:]) +} + +// ToBig returns the uint256 as a stdlib big integer. +// +// Note that this is nearly guaranteed to cause two allocations. Applications +// that are performance sensitive should consider using PutBig instead or avoid +// conversion to big integers altogether when possible. +// +// See PutBig for a variant that allows an existing big integer to be reused +// which can be useful to cut down on the number of allocations by allowing the +// caller to reuse a big integer that already has an internal buffer allocated. +func (n *Uint256) ToBig() *big.Int { + var out big.Int + n.PutBig(&out) + return &out +} + +// SetBig sets the uint256 to the passed standard library big integer modulo +// 2^256. +// +// The resulting uint256 will be set to the 2's complement of the provided value +// when it is negative. +// +// The uint256 is returned to support chaining. This enables syntax like: +// n := new(Uint256).SetBig(n2).AddUint64(1) so that n = n2 + 1 where n2 is not +// modified. +// +// PERFORMANCE NOTE: When the caller expects values to potentially be larger +// than a max uint256, it is _highly_ recommended to reduce the value mod 2^256 +// prior to calling this method for better performance. +// +// The reason is that this method requires an allocation and copy when the +// provided big integer is larger than a max uint256 in order to reduce it +// without modifying the provided arg. The caller can avoid this allocation by +// performing the mod 2^256 prior to calling this method with the value. +// +// More concretely, it is around 3 to 4 times faster to perform the reduction +// caller side as well as avoiding the allocation. +func (n *Uint256) SetBig(n2 *big.Int) *Uint256 { + // Take the value mod 2^256 if needed. + tmp := n2 + if n2.BitLen() > 256 { + tmp = new(big.Int).And(n2, bigUint256Mask) + } + + var buf [32]byte + tmp.FillBytes(buf[:]) // Requires Go 1.15. + n.SetBytes(&buf) + if tmp.Sign() < 0 { + n.Negate() + } + return n +} diff --git a/internal/staging/primitives/uint256/uint256_bench_test.go b/internal/staging/primitives/uint256/uint256_bench_test.go new file mode 100644 index 0000000000..9b4e4b7ef7 --- /dev/null +++ b/internal/staging/primitives/uint256/uint256_bench_test.go @@ -0,0 +1,1325 @@ +// Copyright (c) 2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package uint256 + +import ( + "fmt" + "math/big" + "math/rand" + "testing" + "time" +) + +// These variables are used to help ensure the benchmarks do not elide code. +var ( + noElideBool bool + noElideBytes []byte + noElideInt int + noElideUint16 uint16 + noElideString string +) + +// randBenchVal houses values used throughout the benchmarks that are randomly +// generated with each run to ensure they are not overfitted. +type randBenchVal struct { + buf1 [32]byte + buf2 [32]byte + n1 *Uint256 + n2 *Uint256 + n2Low64 *Uint256 + bigN1 *big.Int + bigN2 *big.Int + bigN2Low64 *big.Int +} + +// randBenchVals houses a slice of the aforementioned randomly-generated values +// to be used throughout the benchmarks to ensure they are not overfitted. +var randBenchVals = func() []randBenchVal { + // Use a unique random seed each benchmark instance. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + var zeroArr [32]byte + + vals := make([]randBenchVal, 512) + for i := 0; i < len(vals); i++ { + val := &vals[i] + if _, err := rng.Read(val.buf1[:]); err != nil { + panic(fmt.Sprintf("failed to read random: %v", err)) + } + for val.buf2 == zeroArr { + if _, err := rng.Read(val.buf2[:]); err != nil { + panic(fmt.Sprintf("failed to read random: %v", err)) + } + } + + val.n1 = new(Uint256).SetBytes(&val.buf1) + val.n2 = new(Uint256).SetBytes(&val.buf2) + val.n2Low64 = new(Uint256).SetUint64(val.n2.Uint64()) + val.bigN1 = new(big.Int).SetBytes(val.buf1[:]) + val.bigN2 = new(big.Int).SetBytes(val.buf2[:]) + val.bigN2Low64 = new(big.Int).SetUint64(val.n2.Uint64()) + } + return vals +}() + +// maxUint256Bytes returns the raw bytes for a max value unsigned 256-bit +// big-endian integer used throughout the benchmarks. +func maxUint256Bytes() []byte { + return hexToBytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +} + +// BenchmarkUint256SetBytes benchmarks initializing an unsigned 256-bit integer +// from bytes in big endian with the specialized type. +func BenchmarkUint256SetBytes(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.SetBytes(&vals[j].buf1) + } + } +} + +// BenchmarkBigIntSetBytes benchmarks initializing an unsigned 256-bit integer +// from bytes in big endian with stdlib big integers. +func BenchmarkBigIntSetBytes(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.SetBytes(vals[j].buf1[:]) + } + } +} + +// BenchmarkUint256SetBytesLE benchmarks initializing an unsigned 256-bit +// integer from bytes in little endian with the specialized type. +func BenchmarkUint256SetBytesLE(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.SetBytesLE(&vals[j].buf1) + } + } +} + +// BenchmarkBigIntSetBytesLE benchmarks initializing an unsigned 256-bit integer +// from bytes in little endian with stdlib big integers. +func BenchmarkBigIntSetBytesLE(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + // The big int API only takes the bytes in big endian, so they need + // to be reversed by the caller. Note that this implementation + // assumes the buffer is already 32 bytes and is not robust against + // other cases, but it's good enough for a reasonable benchmark. + buf := vals[j].buf1[:] + reversed := make([]byte, len(buf)) + blen := len(buf) + for i := 0; i < blen/2; i++ { + reversed[i], reversed[blen-1-i] = buf[blen-1-i], buf[i] + } + n.SetBytes(reversed) + } + } +} + +// BenchmarkUint256Bytes benchmarks unpacking an unsigned 256-bit integer to +// bytes in big endian with the specialized type. +func BenchmarkUint256Bytes(b *testing.B) { + vals := randBenchVals + var buf [32]byte + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + vals[j].n1.PutBytes(&buf) + } + } +} + +// BenchmarkBigIntBytes benchmarks unpacking an unsigned 256-bit integer to +// bytes in big endian with the stdlib big integers. +func BenchmarkBigIntBytes(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + noElideBytes = vals[j].bigN1.Bytes() + } + } +} + +// BenchmarkUint256BytesLE benchmarks unpacking an unsigned 256-bit integer to +// bytes in little endian with the specialized type. +func BenchmarkUint256BytesLE(b *testing.B) { + vals := randBenchVals + var buf [32]byte + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + vals[j].n1.PutBytesLE(&buf) + } + } +} + +// BenchmarkBigIntBytesLE benchmarks unpacking an unsigned 256-bit integer to +// bytes in little endian with the stdlib big integers. +func BenchmarkBigIntBytesLE(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + // The big int API only provides the bytes in big endian, so they + // need to be reversed by the caller. Note that this implementation + // assumes the buffer is already 32 bytes and is not robust against + // other cases, but it's good enough for a reasonable benchmark. + buf := vals[j].bigN1.Bytes() + blen := len(buf) + for i := 0; i < blen/2; i++ { + buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i] + } + noElideBytes = buf + } + } +} + +// BenchmarkUint256Zero benchmarks zeroing an unsigned 256-bit integer with the +// specialized type. +func BenchmarkUint256Zero(b *testing.B) { + n := new(Uint256).SetByteSlice(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + n.Zero() + } +} + +// BenchmarkBigIntZero benchmarks zeroing an unsigned 256-bit integer with +// stdlib big integers. +func BenchmarkBigIntZero(b *testing.B) { + n := new(big.Int).SetBytes(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + n.SetUint64(0) + } +} + +// BenchmarkUint256IsZero benchmarks determining if an unsigned 256-bit integer +// is zero with the specialized type. +func BenchmarkUint256IsZero(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + noElideBool = vals[j].n1.IsZero() + } + } +} + +// BenchmarkBigIntIsZero benchmarks determining if an unsigned 256-bit integer +// is zero with stdlib big integers. +func BenchmarkBigIntIsZero(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + noElideBool = vals[j].bigN1.Sign() == 0 + } + } +} + +// BenchmarkUint256IsOdd benchmarks determining if an unsigned 256-bit integer +// is odd with the specialized type. +func BenchmarkUint256IsOdd(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + noElideBool = vals[j].n1.IsOdd() + } + } +} + +// BenchmarkBigIntIsOdd benchmarks determining if an unsigned 256-bit integer is +// odd with stdlib big integers. +func BenchmarkBigIntIsOdd(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + noElideBool = vals[j].bigN1.Bit(0) == 1 + } + } +} + +// BenchmarkUint256Eq benchmarks determining equality between two unsigned +// 256-bit integers with the specialized type. +func BenchmarkUint256Eq(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.n1.Eq(val.n2) + } + } +} + +// BenchmarkBigIntEq benchmarks determining equality between two unsigned +// 256-bit integers with stdlib big integers. +func BenchmarkBigIntEq(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.bigN1.Cmp(val.bigN2) == 0 + } + } +} + +// BenchmarkUint256Lt benchmarks determining if one unsigned 256-bit integer is +// less than another with the specialized type. +func BenchmarkUint256Lt(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.n1.Lt(val.n2) + } + } +} + +// BenchmarkBigIntLt benchmarks determining if one unsigned 256-bit integer is +// less than another with stdlib big integers. +func BenchmarkBigIntLt(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.bigN1.Cmp(val.bigN2) < 0 + } + } +} + +// BenchmarkUint256Gt benchmarks determining if one unsigned 256-bit integer is +// greater than another with the specialized type. +func BenchmarkUint256Gt(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.n1.Gt(val.n2) + } + } +} + +// BenchmarkBigIntGt benchmarks determining if one unsigned 256-bit integer is +// greater than another with stdlib big integers. +func BenchmarkBigIntGt(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideBool = val.bigN1.Cmp(val.bigN2) > 0 + } + } +} + +// BenchmarkUint256Cmp benchmarks comparing two unsigned 256-bit integers with +// the specialized type. +func BenchmarkUint256Cmp(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideInt = val.n1.Cmp(val.n2) + } + } +} + +// BenchmarkBigIntCmp benchmarks comparing two unsigned 256-bit integers with +// stdlib big integers. +func BenchmarkBigIntCmp(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideInt = val.bigN1.Cmp(val.bigN2) + } + } +} + +// BenchmarkUint256CmpUint64 benchmarks comparing an unsigned 256-bit integer +// with an unsigned 64-bit integer with the specialized type. +func BenchmarkUint256CmpUint64(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideInt = val.n1.CmpUint64(val.n2Low64.Uint64()) + } + } +} + +// BenchmarkBigIntCmp benchmarks comparing an unsigned 256-bit integer with an +// unsigned 64-bit integer with stdlib big integers. +func BenchmarkBigIntCmpUint64(b *testing.B) { + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideInt = val.bigN1.Cmp(val.bigN2Low64) + } + } +} + +// BenchmarkUint256Add benchmarks computing the sum of unsigned 256-bit integers +// with the specialized type. +func BenchmarkUint256Add(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Add2(val.n1, val.n2) + } + } +} + +// BenchmarkBigIntAdd benchmarks computing the sum of unsigned 256-bit integers +// with stdlib big integers. +func BenchmarkBigIntAdd(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Add(val.bigN1, val.bigN2) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256AddUint64 benchmarks computing the sum of an unsigned 256-bit +// integer and an unsigned 64-bit integer with the specialized type. +func BenchmarkUint256AddUint64(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.AddUint64(val.n2Low64.Uint64()) + } + } +} + +// BenchmarkBigIntAddUint64 benchmarks computing the sum of an unsigned 256-bit +// integer and an unsigned 64-bit integer with stdlib big integers. +func BenchmarkBigIntAddUint64(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.Add(n, val.bigN2Low64) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256Sub benchmarks computing the difference of unsigned 256-bit +// integers with the specialized type. +func BenchmarkUint256Sub(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Sub2(val.n1, val.n2) + } + } +} + +// BenchmarkBigIntSub benchmarks computing the difference of unsigned 256-bit +// integers with stdlib big integers. +func BenchmarkBigIntSub(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Sub(val.bigN1, val.bigN2) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256SubUint64 benchmarks computing the difference of an unsigned +// 256-bit integer and unsigned 64-bit integer with the specialized type. +func BenchmarkUint256SubUint64(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.SubUint64(val.n2Low64.Uint64()) + } + } +} + +// BenchmarkBigIntSubUint64 benchmarks computing the difference of an unsigned +// 256-bit integer and unsigned 64-bit integer with stdlib big integers. +func BenchmarkBigIntSubUint64(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.Sub(n, val.bigN2Low64) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256Mul benchmarks computing the product of unsigned 256-bit +// integers with the specialized type. +func BenchmarkUint256Mul(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Mul2(val.n1, val.n2) + } + } +} + +// BenchmarkBigIntMul benchmarks computing the product of unsigned 256-bit +// integers with stdlib big integers. +func BenchmarkBigIntMul(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Mul(val.bigN1, val.bigN2) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256MulUint64 benchmarks computing the product of an unsigned +// 256-bit integer and unsigned 64-bit integer with the specialized type. +func BenchmarkUint256MulUint64(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.MulUint64(val.n2Low64.Uint64()) + } + } +} + +// BenchmarkBigIntMulUint64 benchmarks computing the product of an unsigned +// 256-bit integer and unsigned 64-bit integer with stdlib big integers. +func BenchmarkBigIntMulUint64(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.Mul(n, val.bigN2Low64) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256Square benchmarks computing the quotient of unsigned 256-bit +// integers with the specialized type. +func BenchmarkUint256Square(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.SquareVal(val.n1) + } + } +} + +// BenchmarkBigIntSquare benchmarks computing the quotient of unsigned 256-bit +// integers with stdlib big integers. +func BenchmarkBigIntSquare(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Mul(val.bigN1, val.bigN1) + n.Mod(n, two256) + } + } +} + +// divBenchTest describes tests that are used for the deterministic division +// benchmarks. It is defined separately so the same tests can easily be used in +// comparison benchmarks between the specialized type in this package and stdlib +// big integers. +type divBenchTest struct { + name string // benchmark description + n1 *Uint256 // dividend + n2 *Uint256 // divisor +} + +// makeDivBenches returns a slice of tests that consist of deterministic +// unsigned 256-bit integers of various lengths for use in the division +// benchmarks. +func makeDivBenches() []divBenchTest { + return []divBenchTest{{ + // (1<<256 - 2) / (1<<256 - 1) + name: "dividend lt divisor", + n1: hexToUint256("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"), + n2: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<256 - 1) + name: "dividend eq divisor", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, { + // (1<<64 - 1) / (1<<64 - 255) + name: "1 by 1 near", + n1: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffff01"), + }, { + // (1<<64 - 1) / (1<<2 - 1) + name: "1 by 1 far", + n1: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffffff"), + n2: hexToUint256("0000000000000000000000000000000000000000000000000000000000000003"), + }, { + // (1<<128 - 1) / (1<<64 - 1) + name: "2 by 1 near", + n1: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffffff"), + }, { + // (1<<128 - 1) / (1<<2 - 1) + name: "2 by 1 far", + n1: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000000000000000000000000000000000000000000000000000003"), + }, { + // (1<<192 - 1) / (1<<64 - 1) + name: "3 by 1 near", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffffff"), + }, { + // (1<<192 - 1) / (1<<2 - 1) + name: "3 by 1 far", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000000000000000000000000000000000000000000000000000003"), + }, { + // (1<<256 - 1) / (1<<64 - 1) + name: "4 by 1 near", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000000ffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<2 - 1) + name: "4 by 1 far", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000000000000000000000000000000000000000000000000000003"), + }, { + // (1<<128 - 1) / (1<<128 - 255) + name: "2 by 2 near", + n1: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + n2: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffff01"), + }, { + // (1<<128 - 1) / (1<<65 - 1) + name: "2 by 2 far", + n1: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000001ffffffffffffffff"), + }, { + // (1<<192 - 1) / (1<<128 - 1) + name: "3 by 2 near", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + }, { + // (1<<192 - 1) / (1<<65 - 1) + name: "3 by 2 far", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000001ffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<128 - 1) + name: "4 by 2 near", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<65 - 1) + name: "4 by 2 far", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("000000000000000000000000000000000000000000000001ffffffffffffffff"), + }, { + // (1<<192 - 1) / (1<<192 - 255) + name: "3 by 3 near", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffff01"), + }, { + // (1<<192 - 1) / (1<<129 - 1) + name: "3 by 3 far", + n1: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("00000000000000000000000000000001ffffffffffffffffffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<192 - 1) + name: "4 by 3 near", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<129 - 1) + name: "4 by 3 far", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("00000000000000000000000000000001ffffffffffffffffffffffffffffffff"), + }, { + // (1<<256 - 1) / (1<<256 - 255) + name: "4 by 4 near", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01"), + }, { + // (1<<256 - 1) / (1<<193 - 1) + name: "4 by 4 far", + n1: hexToUint256("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + n2: hexToUint256("0000000000000001ffffffffffffffffffffffffffffffffffffffffffffffff"), + }} +} + +// BenchmarkUint256Div benchmarks computing the quotient of deterministic +// unsigned 256-bit integers of various length with the specialized type. +func BenchmarkUint256Div(b *testing.B) { + benches := makeDivBenches() + for benchIdx := range benches { + bench := benches[benchIdx] + b.Run(bench.name, func(b *testing.B) { + n := new(Uint256) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + n.Div2(bench.n1, bench.n2) + } + }) + } +} + +// BenchmarkUint256DivRandom benchmarks computing the quotient of random large +// unsigned 256-bit integers with the specialized type. +func BenchmarkUint256DivRandom(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Div2(val.n1, val.n2) + } + } +} + +// BenchmarkBigIntDiv benchmarks computing the quotient of deterministic +// unsigned 256-bit integers of various length with stdlib big integers. +func BenchmarkBigIntDiv(b *testing.B) { + benches := makeDivBenches() + for benchIdx := range benches { + bench := benches[benchIdx] + b.Run(bench.name, func(b *testing.B) { + n := new(big.Int) + n1Bytes, n2Bytes := bench.n1.Bytes(), bench.n2.Bytes() + n1 := new(big.Int).SetBytes(n1Bytes[:]) + n2 := new(big.Int).SetBytes(n2Bytes[:]) + + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + n.Div(n1, n2) + } + }) + } +} + +// BenchmarkBigIntDivRandom benchmarks computing the quotient of random large +// unsigned 256-bit integers with stdlib big integers. +func BenchmarkBigIntDivRandom(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Div(val.bigN1, val.bigN2) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256DivUint64 benchmarks computing the quotient of an unsigned +// 256-bit integer and unsigned 64-bit integer with the specialized type. +func BenchmarkUint256DivUint64(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.DivUint64(val.n2Low64.Uint64()) + } + } +} + +// BenchmarkBigIntDivUint64 benchmarks computing the quotient of an unsigned +// 256-bit integer and unsigned 64-bit integer with stdlib big integers. +func BenchmarkBigIntDivUint64(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Div(val.bigN1, val.bigN2Low64) + n.Mod(n, two256) + } + } +} + +// BenchmarkUint256Negate benchmarks computing the negation modulo 2^256 of an +// unsigned 256-bit integer with the specialized type. +func BenchmarkUint256Negate(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.NegateVal(vals[j].n1) + } + } +} + +// BenchmarkBigIntNegate benchmarks computing the negation module 2^256 of an +// unsigned 256-bit integer with stdlib big integers. +func BenchmarkBigIntNegate(b *testing.B) { + n := new(big.Int) + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.Mod(n.Neg(vals[j].bigN1), two256) + } + } +} + +// BenchmarkUint256Lsh benchmarks left shifting an unsigned 256-bit integer with +// the specialized type. +func BenchmarkUint256Lsh(b *testing.B) { + for _, bits := range []uint32{0, 1, 64, 128, 192, 255, 256} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + result := new(Uint256) + max256 := new(Uint256).SetByteSlice(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result.LshVal(max256, bits) + } + }) + } +} + +// BenchmarkBigIntLsh benchmarks left shifting an unsigned 256-bit integer with +// stdlib big integers. +func BenchmarkBigIntLsh(b *testing.B) { + for _, bits := range []uint{0, 1, 64, 128, 192, 255, 256} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + result := new(big.Int) + max256 := new(big.Int).SetBytes(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result.Lsh(max256, bits) + } + }) + } +} + +// BenchmarkUint256Rsh benchmarks right shifting an unsigned 256-bit integer +// with the specialized type. +func BenchmarkUint256Rsh(b *testing.B) { + for _, bits := range []uint32{0, 1, 64, 128, 192, 255, 256} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + result := new(Uint256) + max256 := new(Uint256).SetByteSlice(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result.RshVal(max256, bits) + } + }) + } +} + +// BenchmarkBigIntRsh benchmarks right shifting an unsigned 256-bit integer with +// stdlib big integers. +func BenchmarkBigIntRsh(b *testing.B) { + for _, bits := range []uint{0, 1, 64, 128, 192, 255, 256} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + result := new(big.Int) + max256 := new(big.Int).SetBytes(maxUint256Bytes()) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + result.Rsh(max256, bits) + } + }) + } +} + +// BenchmarkUint256Not benchmarks computing the bitwise not of an unsigned +// 256-bit integer with the specialized type. +func BenchmarkUint256Not(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.Set(vals[j].n1) + n.Not() + } + } +} + +// BenchmarkBigIntNot benchmarks computing the bitwise not of an unsigned +// 256-bit integer with stdlib big integers. +func BenchmarkBigIntNot(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + n.Set(vals[j].bigN1) // For fair comparison. + n.Not(n) + } + } +} + +// BenchmarkUint256Or benchmarks computing the bitwise or of unsigned 256-bit +// integers with the specialized type. +func BenchmarkUint256Or(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.Or(val.n2) + } + } +} + +// BenchmarkBigIntOr benchmarks computing the bitwise or of unsigned 256-bit +// integers with stdlib big integers. +func BenchmarkBigIntOr(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.Or(n, val.bigN2) + } + } +} + +// BenchmarkUint256And benchmarks computing the bitwise and of unsigned 256-bit +// integers with the specialized type. +func BenchmarkUint256And(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.And(val.n2) + } + } +} + +// BenchmarkBigIntAnd benchmarks computing the bitwise and of unsigned 256-bit +// integers with stdlib big integers. +func BenchmarkBigIntAnd(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.And(n, val.bigN2) + } + } +} + +// BenchmarkUint256Xor benchmarks computing the bitwise exclusive or of unsigned +// 256-bit integers with the specialized type. +func BenchmarkUint256Xor(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.n1) + n.Xor(val.n2) + } + } +} + +// BenchmarkBigIntXor benchmarks computing the bitwise exclusive or of unsigned +// 256-bit integers with stdlib big integers. +func BenchmarkBigIntXor(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.Set(val.bigN1) // For fair comparison. + n.Xor(n, val.bigN2) + } + } +} + +// BenchmarkUint256BitLen benchmarks determining the bit length of an unsigned +// 256-bit integer with the specialized type. +func BenchmarkUint256BitLen(b *testing.B) { + for _, bits := range []uint32{64, 128, 192, 255} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + n := new(Uint256).SetUint64(1).Lsh(bits) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + noElideUint16 = n.BitLen() + } + }) + } +} + +// BenchmarkBigIntBitLen benchmarks determining the bit length of an unsigned +// 256-bit integer with stdlib big integers. +func BenchmarkBigIntBitLen(b *testing.B) { + for _, bits := range []uint{64, 128, 192, 255} { + benchName := fmt.Sprintf("bits %d", bits) + b.Run(benchName, func(b *testing.B) { + n := new(big.Int).SetUint64(1) + n.Lsh(n, bits) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + noElideInt = n.BitLen() + } + }) + } +} + +// BenchmarkUint256Text benchmarks converting an unsigned 256-bit integer to +// various output bases with the specialized type. +func BenchmarkUint256Text(b *testing.B) { + vals := randBenchVals + + for _, base := range []OutputBase{2, 8, 10, 16} { + benchName := fmt.Sprintf("base %d", base) + b.Run(benchName, func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideString = val.n1.Text(base) + } + } + }) + } +} + +// BenchmarkBigIntText benchmarks converting an unsigned 256-bit integer to +// various output bases with stdlib big integers. +func BenchmarkBigIntText(b *testing.B) { + vals := randBenchVals + + for _, base := range []int{2, 8, 10, 16} { + benchName := fmt.Sprintf("base %d", base) + b.Run(benchName, func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideString = val.bigN1.Text(base) + } + } + }) + } +} + +// BenchmarkUint256Format benchmarks formatting an unsigned 256-bit integer to +// various output bases via fmt.Sprintf with the specialized type. +func BenchmarkUint256Format(b *testing.B) { + vals := randBenchVals + + for _, base := range []int{2, 8, 10, 16} { + benchName := fmt.Sprintf("base %d", base) + b.Run(benchName, func(b *testing.B) { + var fmtStr string + switch base { + case 2: + fmtStr = "%b" + case 8: + fmtStr = "%o" + case 10: + fmtStr = "%d" + case 16: + fmtStr = "%x" + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideString = fmt.Sprintf(fmtStr, val.n1) + } + } + }) + } +} + +// BenchmarkBigIntFormat benchmarks formatting an unsigned 256-bit integer to +// various output bases via fmt.Sprintf with stdlib big integers. +func BenchmarkBigIntFormat(b *testing.B) { + vals := randBenchVals + + for _, base := range []int{2, 8, 10, 16} { + benchName := fmt.Sprintf("base %d", base) + b.Run(benchName, func(b *testing.B) { + var fmtStr string + switch base { + case 2: + fmtStr = "%b" + case 8: + fmtStr = "%o" + case 10: + fmtStr = "%d" + case 16: + fmtStr = "%x" + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + noElideString = fmt.Sprintf(fmtStr, val.bigN1) + } + } + }) + } +} + +// BenchmarkUint256PutBig benchmarks converting an unsigned 256-bit integer to a +// stdlib big integer. +func BenchmarkUint256PutBig(b *testing.B) { + n := new(big.Int) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + val.n1.PutBig(n) + } + } +} + +// BenchmarkUint256PutBig benchmarks converting a stdlib big integer to an +// unsigned 256-bit integer. +func BenchmarkUint256SetBig(b *testing.B) { + n := new(Uint256) + vals := randBenchVals + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i += len(vals) { + for j := 0; j < len(vals); j++ { + val := &vals[j] + n.SetBig(val.bigN1) + } + } +} diff --git a/internal/staging/primitives/uint256/uint256_test.go b/internal/staging/primitives/uint256/uint256_test.go new file mode 100644 index 0000000000..28c0d79b0d --- /dev/null +++ b/internal/staging/primitives/uint256/uint256_test.go @@ -0,0 +1,4178 @@ +// Copyright (c) 2021 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package uint256 + +import ( + "bytes" + "encoding/hex" + "fmt" + "math/big" + "math/rand" + "reflect" + "testing" + "time" +) + +// hexToBytes converts the passed hex string into bytes and will panic if there +// is an error. This is only provided for the hard-coded constants so errors in +// the source code can be detected. It will only (and must only) be called with +// hard-coded values. +func hexToBytes(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + return b +} + +// hexToUint256 converts the passed hex string into a Uint256 and will panic if +// there is an error. This is only provided for the hard-coded constants so +// errors in the source code can be detected. It will only (and must only) be +// called with hard-coded values. +func hexToUint256(s string) *Uint256 { + if len(s)%2 != 0 { + s = "0" + s + } + b := hexToBytes(s) + if len(b) > 32 { + panic("hex in source file overflows mod 2^256: " + s) + } + return new(Uint256).SetByteSlice(b) +} + +// randBigIntAndUint256 returns a big integer and a uint256 both created from +// the same random value generated by the passed rng. +func randBigIntAndUint256(t *testing.T, rng *rand.Rand) (*big.Int, *Uint256) { + t.Helper() + + var buf [32]byte + if _, err := rng.Read(buf[:]); err != nil { + t.Fatalf("failed to read random: %v", err) + } + + // Create and return both a big integer and a uint256. + bigIntVal := new(big.Int).SetBytes(buf[:]) + ui256Val := new(Uint256).SetBytes(&buf) + return bigIntVal, ui256Val +} + +// TestUint256SetUint64 ensures that setting a scalar to various native integers +// works as expected. +func TestUint256SetUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n uint64 // test value + want [4]uint64 // expected words + }{{ + name: "five", + n: 0x5, + want: [4]uint64{0x5, 0, 0, 0}, + }, { + name: "2^32 - 1", + n: 0xffffffff, + want: [4]uint64{0xffffffff, 0, 0, 0}, + }, { + name: "2^32", + n: 0x100000000, + want: [4]uint64{0x100000000, 0, 0, 0}, + }, { + name: "2^64 - 1", + n: 0xffffffffffffffff, + want: [4]uint64{0xffffffffffffffff, 0, 0, 0}, + }} + + for _, test := range tests { + n := new(Uint256).SetUint64(test.n) + if !reflect.DeepEqual(n.n, test.want) { + t.Errorf("%s: wrong result -- got: %x want: %x", test.name, n.n, + test.want) + continue + } + } +} + +// TestUint256SetBytes ensures that setting a uint256 to a 256-bit big-endian +// unsigned integer via both the slice and array methods works as expected for +// edge cases. Random cases are tested via the various other tests. +func TestUint256SetBytes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + in string // hex encoded test value + want [4]uint64 // expected words + }{{ + name: "empty", + in: "", + want: [4]uint64{0, 0, 0, 0}, + }, { + name: "zero", + in: "00", + want: [4]uint64{0, 0, 0, 0}, + }, { + name: "one", + in: "0000000000000000000000000000000000000000000000000000000000000001", + want: [4]uint64{1, 0, 0, 0}, + }, { + name: "2^64-1 (no leading zeros)", + in: "ffffffffffffffff", + want: [4]uint64{0xffffffffffffffff, 0, 0, 0}, + }, { + name: "2^128-1 (with leading zeros)", + in: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + want: [4]uint64{0xffffffffffffffff, 0xffffffffffffffff, 0, 0}, + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: [4]uint64{ + 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, + }, + }, { + name: "2^8 - 1 (truncated >32 bytes)", + in: "0100000000000000000000000000000000000000000000000000000000000000ff", + want: [4]uint64{0xff, 0, 0, 0}, + }, { + name: "progression", + in: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + want: [4]uint64{ + 0x191a1b1c1d1e1f20, 0x1112131415161718, + 0x090a0b0c0d0e0f10, 0x0102030405060708, + }, + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: [4]uint64{ + 0xa5a5a5a5a5a5a5a5, 0xa5a5a5a5a5a5a5a5, + 0xa5a5a5a5a5a5a5a5, 0xa5a5a5a5a5a5a5a5, + }, + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: [4]uint64{ + 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, + 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, + }, + }} + + for _, test := range tests { + inBytes := hexToBytes(test.in) + + // Ensure setting the bytes via the slice method works as expected. + var n Uint256 + n.SetByteSlice(inBytes) + if !reflect.DeepEqual(n.n, test.want) { + t.Errorf("%s: unexpected result -- got: %x, want: %x", test.name, + n.n, test.want) + continue + } + + // Ensure setting the bytes via the array method works as expected. + var n2 Uint256 + var b32 [32]byte + truncatedInBytes := inBytes + if len(truncatedInBytes) > 32 { + truncatedInBytes = truncatedInBytes[len(truncatedInBytes)-32:] + } + copy(b32[32-len(truncatedInBytes):], truncatedInBytes) + n2.SetBytes(&b32) + if !reflect.DeepEqual(n2.n, test.want) { + t.Errorf("%s: unexpected result -- got: %x, want: %x", test.name, + n2.n, test.want) + continue + } + } +} + +// TestUint256SetBytesLE ensures that setting a uint256 to a 256-bit +// little-endian unsigned integer via both the slice and array methods works as +// expected for edge cases. +func TestUint256SetBytesLE(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + in string // hex encoded test value + want [4]uint64 // expected words + }{{ + name: "empty", + in: "", + want: [4]uint64{0, 0, 0, 0}, + }, { + name: "zero", + in: "00", + want: [4]uint64{0, 0, 0, 0}, + }, { + name: "one", + in: "01", + want: [4]uint64{1, 0, 0, 0}, + }, { + name: "2^64-1 (no trailing zeros)", + in: "ffffffffffffffff", + want: [4]uint64{0xffffffffffffffff, 0, 0, 0}, + }, { + name: "2^128-1 (with trailing zeros)", + in: "ffffffffffffffffffffffffffffffff00000000000000000000000000000000", + want: [4]uint64{0xffffffffffffffff, 0xffffffffffffffff, 0, 0}, + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: [4]uint64{ + 0xffffffffffffffff, 0xffffffffffffffff, + 0xffffffffffffffff, 0xffffffffffffffff, + }, + }, { + name: "one (truncated >32 bytes)", + in: "0100000000000000000000000000000000000000000000000000000000000000ff", + want: [4]uint64{1, 0, 0, 0}, + }, { + name: "progression", + in: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + want: [4]uint64{ + 0x0807060504030201, 0x100f0e0d0c0b0a09, + 0x1817161514131211, 0x201f1e1d1c1b1a19, + }, + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: [4]uint64{ + 0xa5a5a5a5a5a5a5a5, 0xa5a5a5a5a5a5a5a5, + 0xa5a5a5a5a5a5a5a5, 0xa5a5a5a5a5a5a5a5, + }, + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: [4]uint64{ + 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, + 0x5a5a5a5a5a5a5a5a, 0x5a5a5a5a5a5a5a5a, + }, + }} + + for _, test := range tests { + inBytes := hexToBytes(test.in) + + // Ensure setting the bytes via the slice method works as expected. + var n Uint256 + n.SetByteSliceLE(inBytes) + if !reflect.DeepEqual(n.n, test.want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n.n, test.want) + continue + } + + // Ensure setting the bytes via the array method works as expected. + var n2 Uint256 + var b32 [32]byte + truncatedInBytes := inBytes + if len(truncatedInBytes) > 32 { + truncatedInBytes = truncatedInBytes[:32] + } + copy(b32[:], truncatedInBytes) + n2.SetBytesLE(&b32) + if !reflect.DeepEqual(n2.n, test.want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n2.n, test.want) + continue + } + } +} + +// TestUint256Bytes ensures that retrieving the bytes for a uint256 encoded as a +// 256-bit big-endian unsigned integer via the various methods works as expected +// for edge cases. Random cases are tested via the various other tests. +func TestUint256Bytes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + in string // hex encoded test value + want string // expected hex encoded bytes + }{{ + name: "zero", + in: "0", + want: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "one", + in: "1", + want: "0000000000000000000000000000000000000000000000000000000000000001", + }, { + name: "2^64 - 1", + in: "000000000000000000000000000000000000000000000000ffffffffffffffff", + want: "000000000000000000000000000000000000000000000000ffffffffffffffff", + }, { + name: "2^128 - 1", + in: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + want: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + }, { + name: "2^192 - 1", + in: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + }} + + for _, test := range tests { + n := hexToUint256(test.in) + want := hexToBytes(test.want) + + // Ensure getting the bytes works as expected. + gotBytes := n.Bytes() + if !bytes.Equal(gotBytes[:], want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + gotBytes, want) + continue + } + + // Ensure getting the bytes directly into an array works as expected. + var b32 [32]byte + n.PutBytes(&b32) + if !bytes.Equal(b32[:], want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + b32, want) + continue + } + + // Ensure getting the bytes directly into a slice works as expected. + var buffer [64]byte + n.PutBytesUnchecked(buffer[:]) + if !bytes.Equal(buffer[:32], want) { + t.Errorf("%q: unexpected result, got: %x, want: %x", test.name, + buffer[:32], want) + continue + } + } +} + +// TestUint256BytesLE ensures that retrieving the bytes for a uint256 encoded as +// a 256-bit little-endian unsigned integer via the various methods works as +// expected for edge cases. +func TestUint256BytesLE(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + in string // hex encoded test value + want string // expected hex encoded bytes + }{{ + name: "zero", + in: "0", + want: "0000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "one", + in: "1", + want: "0100000000000000000000000000000000000000000000000000000000000000", + }, { + name: "2^64 - 1", + in: "000000000000000000000000000000000000000000000000ffffffffffffffff", + want: "ffffffffffffffff000000000000000000000000000000000000000000000000", + }, { + name: "2^128 - 1", + in: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffff00000000000000000000000000000000", + }, { + name: "2^192 - 1", + in: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000", + }, { + name: "2^256 - 1", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits", + in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + }, { + name: "alternating bits 2", + in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + }} + + for _, test := range tests { + n := hexToUint256(test.in) + want := hexToBytes(test.want) + + // Ensure getting the bytes works as expected. + gotBytes := n.BytesLE() + if !bytes.Equal(gotBytes[:], want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + gotBytes, want) + continue + } + + // Ensure getting the bytes directly into an array works as expected. + var b32 [32]byte + n.PutBytesLE(&b32) + if !bytes.Equal(b32[:], want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + b32, want) + continue + } + + // Ensure getting the bytes directly into a slice works as expected. + var buffer [64]byte + n.PutBytesUncheckedLE(buffer[:]) + if !bytes.Equal(buffer[:32], want) { + t.Errorf("%q: unexpected result, got: %x, want: %x", test.name, + buffer[:32], want) + continue + } + } +} + +// TestUint256Zero ensures that zeroing a uint256 works as expected. +func TestUint256Zero(t *testing.T) { + t.Parallel() + + n := hexToUint256("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5") + n.Zero() + for idx, word := range n.n { + if word != 0 { + t.Errorf("internal word at index #%d is not zero - got %d", idx, + word) + } + } +} + +// TestUint256IsZero ensures that checking if a uint256 is zero works as +// expected. +func TestUint256IsZero(t *testing.T) { + t.Parallel() + + var n Uint256 + if !n.IsZero() { + t.Fatalf("new uint256 is not zero - got %v (words %x)", n, n.n) + } + + n.SetUint64(1) + if n.IsZero() { + t.Fatalf("claims zero for nonzero uint256 - got %v (words %x)", n, n.n) + } + + n.SetUint64(0) + if !n.IsZero() { + t.Fatalf("claims nonzero for zero uint256 - got %v (words %x)", n, n.n) + } + + n.SetUint64(1) + n.Zero() + if !n.IsZero() { + t.Fatalf("claims zero for nonzero uint256 - got %v (words %x)", n, n.n) + } +} + +// TestUint256IsOdd ensures that checking if a uint256 is odd works as expected. +func TestUint256IsOdd(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want bool // expected oddness + }{{ + name: "zero", + n: "0", + want: false, + }, { + name: "one", + n: "1", + want: true, + }, { + name: "two", + n: "2", + want: false, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: true, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: false, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: true, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: false, + }} + + for _, test := range tests { + got := hexToUint256(test.n).IsOdd() + if got != test.want { + t.Errorf("%q: wrong result -- got: %v, want: %v", test.name, got, + test.want) + continue + } + } +} + +// TestUint256IsUint32 ensures that checking if a uint256 can be represented as +// a uint32 without loss of precision works as expected. +func TestUint256IsUint32(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want bool // expected result + }{{ + name: "zero", + n: "0", + want: true, + }, { + name: "one", + n: "1", + want: true, + }, { + name: "two", + n: "2", + want: true, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: true, + }, { + name: "2^32", + n: "100000000", + want: false, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: false, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: false, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: false, + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: false, + }} + + for _, test := range tests { + got := hexToUint256(test.n).IsUint32() + if got != test.want { + t.Errorf("%q: wrong result -- got: %v, want: %v", test.name, got, + test.want) + continue + } + } +} + +// TestUint256Uint32 ensures that treating a uint256 as a uint32 produces the +// expected result. +func TestUint256Uint32(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want uint32 // expected uint32 + }{{ + name: "zero", + n: "0", + want: 0, + }, { + name: "one", + n: "1", + want: 1, + }, { + name: "two", + n: "2", + want: 2, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: 0xffffffff, + }, { + name: "2^32", + n: "100000000", + want: 0, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: 0xfffffffe, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: 0xffffffff, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: 0, + }, { + name: "2^192 - 2^16 + 1", + n: "ffffffffffffffffffffffffffffffffffffffffffff0000", + want: 0xffff0000, + }, { + name: "2^256 - 2^31", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffff", + want: 0x7fffffff, + }} + + for _, test := range tests { + got := hexToUint256(test.n).Uint32() + if got != test.want { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + test.want) + continue + } + } +} + +// TestUint256IsUint64 ensures that checking if a uint256 can be represented as +// a uint64 without loss of precision works as expected. +func TestUint256IsUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want bool // expected result + }{{ + name: "zero", + n: "0", + want: true, + }, { + name: "one", + n: "1", + want: true, + }, { + name: "two", + n: "2", + want: true, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: true, + }, { + name: "2^32", + n: "100000000", + want: true, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: true, + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: true, + }, { + name: "2^64", + n: "10000000000000000", + want: false, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: false, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: false, + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: false, + }} + + for _, test := range tests { + got := hexToUint256(test.n).IsUint64() + if got != test.want { + t.Errorf("%q: wrong result -- got: %v, want: %v", test.name, got, + test.want) + continue + } + } +} + +// TestUint256Uint64 ensures that treating a uint256 as a uint64 produces the +// expected result. +func TestUint256Uint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want uint64 // expected uint64 + }{{ + name: "zero", + n: "0", + want: 0, + }, { + name: "one", + n: "1", + want: 1, + }, { + name: "two", + n: "2", + want: 2, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: 0xffffffff, + }, { + name: "2^32", + n: "100000000", + want: 0x100000000, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: 0xfffffffffffffffe, + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: 0xffffffffffffffff, + }, { + name: "2^64", + n: "10000000000000000", + want: 0, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: 0xffffffffffffffff, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: 0, + }, { + name: "2^192 - 2^16 + 1", + n: "ffffffffffffffffffffffffffffffffffffffffffff0000", + want: 0xffffffffffff0000, + }, { + name: "2^256 - 2^63", + n: "ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffff", + want: 0x7fffffffffffffff, + }} + + for _, test := range tests { + got := hexToUint256(test.n).Uint64() + if got != test.want { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + test.want) + continue + } + } +} + +// TestUint256Comparison ensures that comparing two uint256s via the various +// comparison operators works as expected for edge cases. +func TestUint256Comparison(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // hex encoded value + n2 string // hex encoded value + wantCmp int // expected comparison result + }{{ + name: "0 vs 0", + n1: "0", + n2: "0", + wantCmp: 0, + }, { + name: "0 vs 1", + n1: "0", + n2: "1", + wantCmp: -1, + }, { + name: "1 vs 0", + n1: "1", + n2: "0", + wantCmp: 1, + }, { + name: "2^32 - 1 vs 2^32 - 1", + n1: "ffffffff", + n2: "ffffffff", + wantCmp: 0, + }, { + name: "2^32 - 1 vs 2^32", + n1: "ffffffff", + n2: "100000000", + wantCmp: -1, + }, { + name: "2^32 vs 2^32 - 1", + n1: "100000000", + n2: "ffffffff", + wantCmp: 1, + }, { + name: "2^32 vs 2^32", + n1: "100000000", + n2: "100000000", + wantCmp: 0, + }, { + name: "2^64 - 1 vs 2^64 - 2", + n1: "ffffffffffffffff", + n2: "fffffffffffffffe", + wantCmp: 1, + }, { + name: "2^64 - 1 vs 2^64 - 1", + n1: "ffffffffffffffff", + n2: "ffffffffffffffff", + wantCmp: 0, + }, { + name: "2^64 - 1 vs 2^64", + n1: "ffffffffffffffff", + n2: "10000000000000000", + wantCmp: -1, + }, { + name: "2^64 vs 2^64 - 1", + n1: "10000000000000000", + n2: "ffffffffffffffff", + wantCmp: 1, + }, { + name: "2^64 vs 2^64", + n1: "10000000000000000", + n2: "10000000000000000", + wantCmp: 0, + }, { + name: "2^128 - 1 vs 2^128 - 1", + n1: "ffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffff", + wantCmp: 0, + }, { + name: "2^128 - 1 vs 2^128", + n1: "ffffffffffffffffffffffffffffffff", + n2: "100000000000000000000000000000000", + wantCmp: -1, + }, { + name: "2^128 vs 2^128 - 1", + n1: "100000000000000000000000000000000", + n2: "ffffffffffffffffffffffffffffffff", + wantCmp: 1, + }, { + name: "2^128 vs 2^128", + n1: "100000000000000000000000000000000", + n2: "100000000000000000000000000000000", + wantCmp: 0, + }, { + name: "2^192 - 1 vs 2^192 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffff", + wantCmp: 0, + }, { + name: "2^192 - 1 vs 2^192", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "1000000000000000000000000000000000000000000000000", + wantCmp: -1, + }, { + name: "2^192 vs 2^192 - 1", + n1: "1000000000000000000000000000000000000000000000000", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffff", + wantCmp: 1, + }, { + name: "2^192 vs 2^192", + n1: "1000000000000000000000000000000000000000000000000", + n2: "1000000000000000000000000000000000000000000000000", + wantCmp: 0, + }, { + name: "2^256 - 1 vs 2^256 - 2", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + wantCmp: 1, + }, { + name: "2^256 - 1 vs 2^256 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + wantCmp: 0, + }} + + for _, test := range tests { + // Ensure comparing the numbers produces the expected cmp result. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + gotCmp := n1.Cmp(n2) + if gotCmp != test.wantCmp { + t.Errorf("%q: incorrect cmp result -- got: %v, want: %v", test.name, + gotCmp, test.wantCmp) + continue + } + + // Ensure comparing the numbers produces the expected == result. + isEq := n1.Eq(n2) + wantEq := test.wantCmp == 0 + if isEq != wantEq { + t.Errorf("%q: incorrect == result -- got: %v, want: %v", test.name, + isEq, wantEq) + continue + } + + // Ensure comparing the numbers produces the expected < result. + isLt := n1.Lt(n2) + wantLt := test.wantCmp < 0 + if isLt != wantLt { + t.Errorf("%q: incorrect < result -- got: %v, want: %v", test.name, + isLt, wantLt) + continue + } + + // Ensure comparing the numbers produces the expected <= result. + isLtEq := n1.LtEq(n2) + wantLtEq := test.wantCmp <= 0 + if isLtEq != wantLtEq { + t.Errorf("%q: incorrect <= result -- got: %v, want: %v", test.name, + isLtEq, wantLtEq) + continue + } + + // Ensure comparing the numbers produces the expected > result. + isGt := n1.Gt(n2) + wantGt := test.wantCmp > 0 + if isGt != wantGt { + t.Errorf("%q: incorrect > result -- got: %v, want: %v", test.name, + isGt, wantGt) + continue + } + + // Ensure comparing the numbers produces the expected >= result. + isGtEq := n1.GtEq(n2) + wantGtEq := test.wantCmp >= 0 + if isGtEq != wantGtEq { + t.Errorf("%q: incorrect >= result -- got: %v, want: %v", test.name, + isGtEq, wantGtEq) + continue + } + } +} + +// TestUint256ComparisonRandom ensures that comparing two uint256s created from +// random values via the various comparison operators works as expected. +func TestUint256ComparisonRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Ensure the uint256s are equal to themselves. + if !n1.Eq(n1) { + t.Fatalf("failed equality check -- n1: %x", n1) + } + if !n2.Eq(n2) { + t.Fatalf("failed equality check -- n2: %x", n2) + } + + // Ensure the uint256 comparison result matches the one using big ints. + bigCmpResult := bigN1.Cmp(bigN2) + cmpResult := n1.Cmp(n2) + if cmpResult != bigCmpResult { + t.Errorf("incorrect cmp result n1: %x, n2: %x -- got %v, want %v", + n1, n2, cmpResult, bigCmpResult) + } + + // Ensure comparing the numbers produces the expected == result. + isEq := n1.Eq(n2) + wantEq := bigCmpResult == 0 + if isEq != wantEq { + t.Errorf("incorrect == result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isEq, wantEq) + continue + } + + // Ensure comparing the numbers produces the expected < result. + isLt := n1.Lt(n2) + wantLt := bigCmpResult < 0 + if isLt != wantLt { + t.Errorf("incorrect < result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isLt, wantLt) + continue + } + + // Ensure comparing the numbers produces the expected <= result. + isLtEq := n1.LtEq(n2) + wantLtEq := bigCmpResult <= 0 + if isLtEq != wantLtEq { + t.Errorf("incorrect <= result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isLtEq, wantLtEq) + continue + } + + // Ensure comparing the numbers produces the expected > result. + isGt := n1.Gt(n2) + wantGt := bigCmpResult > 0 + if isGt != wantGt { + t.Errorf("incorrect > result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isGt, wantGt) + continue + } + + // Ensure comparing the numbers produces the expected >= result. + isGtEq := n1.GtEq(n2) + wantGtEq := bigCmpResult >= 0 + if isGtEq != wantGtEq { + t.Errorf("incorrect >= result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isGtEq, wantGtEq) + continue + } + } +} + +// TestUint256ComparisonUint64 ensures that comparing a uint256 and a uint64 via +// the various comparison operators works as expected for edge cases. +func TestUint256ComparisonUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // hex encoded value + n2 uint64 // uint64 to compare with + wantCmp int // expected comparison result + }{{ + name: "0 vs 0", + n1: "0", + n2: 0, + wantCmp: 0, + }, { + name: "0 vs 1", + n1: "0", + n2: 1, + wantCmp: -1, + }, { + name: "1 vs 0", + n1: "1", + n2: 0, + wantCmp: 1, + }, { + name: "2^32 - 1 vs 2^32 - 1", + n1: "ffffffff", + n2: 0xffffffff, + wantCmp: 0, + }, { + name: "2^32 - 1 vs 2^32", + n1: "ffffffff", + n2: 0x100000000, + wantCmp: -1, + }, { + name: "2^32 vs 2^32 - 1", + n1: "100000000", + n2: 0xffffffff, + wantCmp: 1, + }, { + name: "2^32 vs 2^32", + n1: "100000000", + n2: 0x100000000, + wantCmp: 0, + }, { + name: "2^64 - 1 vs 2^64 - 2", + n1: "ffffffffffffffff", + n2: 0xfffffffffffffffe, + wantCmp: 1, + }, { + name: "2^64 - 1 vs 2^64 - 1", + n1: "ffffffffffffffff", + n2: 0xffffffffffffffff, + wantCmp: 0, + }, { + name: "2^64 vs 2^64 - 1", + n1: "10000000000000000", + n2: 0xffffffffffffffff, + wantCmp: 1, + }, { + name: "2^128 - 1 vs 2^64 - 1", + n1: "ffffffffffffffffffffffffffffffff", + n2: 0xffffffffffffffff, + wantCmp: 1, + }, { + name: "2^128 vs 0", + n1: "100000000000000000000000000000000", + n2: 0, + wantCmp: 1, + }, { + name: "2^192 - 1 vs 2^64 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 0xffffffffffffffff, + wantCmp: 1, + }, { + name: "2^192 vs 0", + n1: "1000000000000000000000000000000000000000000000000", + n2: 0, + wantCmp: 1, + }, { + name: "2^256 - 1 vs 2^64 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 0xffffffffffffffff, + wantCmp: 1, + }} + + for _, test := range tests { + // Ensure comparing the numbers produces the expected cmp result. + n1 := hexToUint256(test.n1) + gotCmp := n1.CmpUint64(test.n2) + if gotCmp != test.wantCmp { + t.Errorf("%q: incorrect cmp result -- got: %v, want: %v", test.name, + gotCmp, test.wantCmp) + continue + } + + // Ensure comparing the numbers produces the expected == result. + isEq := n1.EqUint64(test.n2) + wantEq := test.wantCmp == 0 + if isEq != wantEq { + t.Errorf("%q: incorrect == result -- got: %v, want: %v", test.name, + isEq, wantEq) + continue + } + + // Ensure comparing the numbers produces the expected < result. + isLt := n1.LtUint64(test.n2) + wantLt := test.wantCmp < 0 + if isLt != wantLt { + t.Errorf("%q: incorrect < result -- got: %v, want: %v", test.name, + isLt, wantLt) + continue + } + + // Ensure comparing the numbers produces the expected <= result. + isLtEq := n1.LtEqUint64(test.n2) + wantLtEq := test.wantCmp <= 0 + if isLtEq != wantLtEq { + t.Errorf("%q: incorrect <= result -- got: %v, want: %v", test.name, + isLt, wantLtEq) + continue + } + + // Ensure comparing the numbers produces the expected > result. + isGt := n1.GtUint64(test.n2) + wantGt := test.wantCmp > 0 + if isGt != wantGt { + t.Errorf("%q: incorrect > result -- got: %v, want: %v", test.name, + isGt, wantGt) + continue + } + + // Ensure comparing the numbers produces the expected >= result. + isGtEq := n1.GtEqUint64(test.n2) + wantGtEq := test.wantCmp >= 0 + if isGtEq != wantGtEq { + t.Errorf("%q: incorrect >= result -- got: %v, want: %v", test.name, + isGtEq, wantGtEq) + continue + } + } +} + +// TestUint256ComparisonUint64Random ensures that comparing a uint256 vs uint64 +// created from random values via the various comparison operators works as +// expected. +func TestUint256ComparisonUint64Random(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate two big integer and uint256/uint64 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + n2 := rng.Uint64() + bigN2 := new(big.Int).SetUint64(n2) + + // Ensure a the uint256 truncated to 64 bits equals that truncated + // 64-bit result. + truncatedN1Uint64 := n1.Uint64() + truncatedN1 := new(Uint256).SetUint64(truncatedN1Uint64) + if !truncatedN1.EqUint64(truncatedN1Uint64) { + t.Fatalf("failed equality check -- n: %x", truncatedN1) + } + + // Ensure the uint256 comparison result matches the one using big ints. + bigCmpResult := bigN1.Cmp(bigN2) + cmpResult := n1.CmpUint64(n2) + if cmpResult != bigCmpResult { + t.Errorf("incorrect cmp result n1: %x, n2: %x -- got %v, want %v", + n1, n2, cmpResult, bigCmpResult) + } + + // Ensure comparing the numbers produces the expected == result. + isEq := n1.EqUint64(n2) + wantEq := bigCmpResult == 0 + if isEq != wantEq { + t.Errorf("incorrect == result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isEq, wantEq) + continue + } + + // Ensure comparing the numbers produces the expected < result. + isLt := n1.LtUint64(n2) + wantLt := bigCmpResult < 0 + if isLt != wantLt { + t.Errorf("incorrect < result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isLt, wantLt) + continue + } + + // Ensure comparing the numbers produces the expected <= result. + isLtEq := n1.LtEqUint64(n2) + wantLtEq := bigCmpResult <= 0 + if isLtEq != wantLtEq { + t.Errorf("incorrect <= result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isLtEq, wantLtEq) + continue + } + + // Ensure comparing the numbers produces the expected > result. + isGt := n1.GtUint64(n2) + wantGt := bigCmpResult > 0 + if isGt != wantGt { + t.Errorf("incorrect > result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isGt, wantGt) + continue + } + + // Ensure comparing the numbers produces the expected >= result. + isGtEq := n1.GtEqUint64(n2) + wantGtEq := bigCmpResult >= 0 + if isGtEq != wantGtEq { + t.Errorf("incorrect >= result n1: %x, n2: %x -- got: %v, want: %v", + n1, n2, isGtEq, wantGtEq) + continue + } + } +} + +// TestUint256Add ensures that adding two uint256s works as expected for edge +// cases. +func TestUint256Add(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 string // second hex encoded test value + want string // expected hex encoded result + }{{ + name: "zero + one", + n1: "0", + n2: "1", + want: "1", + }, { + name: "one + zero", + n1: "1", + n2: "0", + want: "1", + }, { + name: "max uint256 + 1 (carry in all words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "1", + want: "0", + }, { + name: "max uint256 + 2 (carry in all words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "2", + want: "1", + }, { + name: "(2^64 - 15) + 16 (carry in word zero)", + n1: "fffffffffffffff0", + n2: "10", + want: "10000000000000000", + }, { + name: "carry in word one", + n1: "0000ffff000000000000000000000000", + n2: "ffff0001000000000000000000000000", + want: "100000000000000000000000000000000", + }, { + name: "carry in word two", + n1: "0000ffff0000000000000000000000000000000000000000", + n2: "ffff00010000000000000000000000000000000000000000", + want: "1000000000000000000000000000000000000000000000000", + }, { + name: "carry in word three", + n1: "0000ffff00000000000000000000000000000000000000000000000000000000", + n2: "ffff000100000000000000000000000000000000000000000000000000000000", + want: "0", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure adding the two values produces the expected result. + got := new(Uint256).Add2(n1, n2) + if !got.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + got, want) + continue + } + + // Ensure single argument adding also produces the expected result. + n1.Add(n2) + if !n1.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n1, want) + continue + } + } +} + +// TestUint256AddRandom ensures that adding two uint256s created from random +// values together works as expected by also performing the same operation with +// big ints and comparing the results. +func TestUint256AddRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the sum of the values using big ints. + bigIntResult := new(big.Int).Add(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the sum of the values using uint256s. + uint256Result := new(Uint256).Add2(n1, n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched add n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256AddUint64 ensures that adding a uint64 to a uint256 works as +// expected for edge cases. +func TestUint256AddUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 uint64 // uint64 test value + want string // expected hex encoded result + }{{ + name: "zero + one", + n1: "0", + n2: 1, + want: "1", + }, { + name: "one + zero", + n1: "1", + n2: 0, + want: "1", + }, { + name: "max uint256 + 1 (carry in all words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 1, + want: "0", + }, { + name: "max uint256 + 2 (carry in all words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 2, + want: "1", + }, { + name: "(2^64 - 15) + 16 (carry in word zero)", + n1: "fffffffffffffff0", + n2: 0x10, + want: "10000000000000000", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: 0x5a5a5a5a5a5a5a5a, + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5ffffffffffffffff", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: 0xa5a5a5a5a5a5a5a5, + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5affffffffffffffff", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + want := hexToUint256(test.want) + + // Ensure the result is the expected value. + n1.AddUint64(test.n2) + if !n1.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n1, want) + continue + } + } +} + +// TestUint256AddUint64Random ensures that adding a uint64 to a uint256 +// together, both created from random values, works as expected by also +// performing the same operation with big ints and comparing the results. +func TestUint256AddUint64Random(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + n2 := rng.Uint64() + bigN2 := new(big.Int).SetUint64(n2) + + // Calculate the sum of the values using big ints. + bigIntResult := new(big.Int).Add(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the sum of the values using uint256s. + uint256Result := new(Uint256).Set(n1).AddUint64(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched add n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256Sub ensures that subtracting two uint256s works as expected for +// edge cases. +func TestUint256Sub(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 string // second hex encoded test value + want string // expected hex encoded result + }{{ + name: "zero - one (borrow in all words)", + n1: "0", + n2: "1", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "one - zero", + n1: "1", + n2: "0", + want: "1", + }, { + name: "max uint256 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "1", + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^64 + 15) - 16 (borrow in word zero)", + n1: "1000000000000000f", + n2: "10", + want: "ffffffffffffffff", + }, { + name: "borrow in word one", + n1: "100000000000000000000000000000000", + n2: "080000000000000000000000000000000", + want: "80000000000000000000000000000000", + }, { + name: "borrow in word two", + n1: "1000000000000000000000000000000000000000000000000", + n2: "0800000000000000000000000000000000000000000000000", + want: "800000000000000000000000000000000000000000000000", + }, { + name: "borrow in word three", + n1: "6000000000000000000000000000000000000000000000000000000000000000", + n2: "8000000000000000000000000000000000000000000000000000000000000000", + want: "e000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b4b5", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure subtracting the two values produces the expected result. + got := new(Uint256).Sub2(n1, n2) + if !got.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + got, want) + continue + } + + // Ensure single argument subtracting also produces the expected result. + n1.Sub(n2) + if !n1.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n1, want) + continue + } + } +} + +// TestUint256SubRandom ensures that subtracting two uint256s created from +// random values works as expected by also performing the same operation with +// big ints and comparing the results. +func TestUint256SubRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the difference of the values using big ints. + bigIntResult := new(big.Int).Sub(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the difference of the values using uint256s. + uint256Result := new(Uint256).Sub2(n1, n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched sub n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256SubUint64 ensures that adding a uint64 to a uint256 works as +// expected for edge cases. +func TestUint256SubUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 uint64 // uint64 test value + want string // expected hex encoded result + }{{ + name: "zero - one (borrow in all words)", + n1: "0", + n2: 1, + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "one - zero", + n1: "1", + n2: 0, + want: "1", + }, { + name: "max uint256 - 1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 1, + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^64 + 15) - 16 (borrow in word zero)", + n1: "1000000000000000f", + n2: 0x10, + want: "ffffffffffffffff", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: 0x5a5a5a5a5a5a5a5a, + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a54b4b4b4b4b4b4b4b", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: 0xa5a5a5a5a5a5a5a5, + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a59b4b4b4b4b4b4b4b5", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + want := hexToUint256(test.want) + + // Ensure the result is the expected value. + n1.SubUint64(test.n2) + if !n1.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + n1, want) + continue + } + } +} + +// TestUint256SubUint64Random ensures that subtracting a uint64 from a uint256 +// together, both created from random values, works as expected by also +// performing the same operation with big ints and comparing the results. +func TestUint256SubUint64Random(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + n2 := rng.Uint64() + bigN2 := new(big.Int).SetUint64(n2) + + // Calculate the difference of the values using big ints. + bigIntResult := new(big.Int).Sub(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the difference of the values using uint256s. + uint256Result := new(Uint256).Set(n1).SubUint64(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched sub n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256Mul ensures that multiplying two uint256s together works as +// expected for edge cases. +func TestUint256Mul(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded value + n2 string // second hex encoded value to multiply with + want string // expected hex encoded value + }{{ + name: "zero * zero", + n1: "0", + n2: "0", + want: "0", + }, { + name: "zero * one", + n1: "0", + n2: "1", + want: "0", + }, { + name: "one * zero", + n1: "1", + n2: "0", + want: "0", + }, { + name: "one * one", + n1: "1", + n2: "1", + want: "1", + }, { + name: "(2^64 - 1) * 2 (carry to word 1)", + n1: "ffffffffffffffff", + n2: "2", + want: "1fffffffffffffffe", + }, { + name: "(2^128 - 1) * 2 (carry to word 2)", + n1: "ffffffffffffffffffffffffffffffff", + n2: "2", + want: "1fffffffffffffffffffffffffffffffe", + }, { + name: "(2^192 - 1) * 2 (carry to word 3)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "2", + want: "1fffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^256 - 1) * 2 (carry to word 4)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "2", + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^256 - 1) * (2^256 - 1) (carry through all 8 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "1", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "4d10d4985c1fe3a76b2ef2b67a3e01c5894d10d4985c1fe3a76b2ef2b67a3e02", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "4d10d4985c1fe3a76b2ef2b67a3e01c5894d10d4985c1fe3a76b2ef2b67a3e02", + }} + + for _, test := range tests { + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure multiplying two other values produces the expected result. + got := new(Uint256).Mul2(n1, n2) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + + // Ensure single argument multiplying also produces the expected result. + n1.Mul(n2) + if !n1.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n1, + want) + continue + } + } +} + +// TestUint256MulRandom ensures that multiplying two uint256s created from +// random values together works as expected by also performing the same +// operation with big ints and comparing the results. +func TestUint256MulRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the product of the values using big ints. + bigIntResult := new(big.Int).Mul(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the product of the values using uint256s. + uint256Result := new(Uint256).Mul2(n1, n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched mul n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256MulUint64 ensures that multiplying a uint256 by a uint64 works as +// expected for edge cases. +func TestUint256MulUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded value + n2 uint64 // uint64 to multiply with + want string // expected hex encoded value + }{{ + name: "zero * zero", + n1: "0", + n2: 0, + want: "0", + }, { + name: "zero * one", + n1: "0", + n2: 1, + want: "0", + }, { + name: "one * zero", + n1: "1", + n2: 0, + want: "0", + }, { + name: "one * one", + n1: "1", + n2: 1, + want: "1", + }, { + name: "(2^64 - 1) * 2 (carry to word 1)", + n1: "ffffffffffffffff", + n2: 2, + want: "1fffffffffffffffe", + }, { + name: "(2^128 - 1) * 2 (carry to word 2)", + n1: "ffffffffffffffffffffffffffffffff", + n2: 2, + want: "1fffffffffffffffffffffffffffffffe", + }, { + name: "(2^192 - 1) * 2 (carry to word 3)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 2, + want: "1fffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^256 - 1) * 2 (carry to word 4)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 2, + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^256 - 1) * (2^64 - 1) (max uint64)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 0xffffffffffffffff, + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000001", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: 0x5a5a5a5a5a5a5a5a, + want: "e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1a76b2ef2b67a3e02", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: 0xa5a5a5a5a5a5a5a5, + want: "e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1a76b2ef2b67a3e02", + }} + + for _, test := range tests { + n1 := hexToUint256(test.n1) + want := hexToUint256(test.want) + + // Ensure single multiplying produces the expected result. + n1.MulUint64(test.n2) + if !n1.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n1, + want) + continue + } + } +} + +// TestUint256MulUint64Random ensures that multiplying a uint256 by a uint64, +// both created from random values, works as expected by also performing the +// same operation with big ints and comparing the results. +func TestUint256MulUint64Random(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + n2 := rng.Uint64() + bigN2 := new(big.Int).SetUint64(n2) + + // Calculate the product of the values using big ints. + bigIntResult := new(big.Int).Mul(bigN1, bigN2) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the product of the values using uint256s. + uint256Result := new(Uint256).Set(n1).MulUint64(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched mul n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256Square ensures that squaring uint256s works as expected for edge +// cases. +func TestUint256Square(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + want string // expected hex encoded value + }{{ + name: "zero", + n: "0", + want: "0", + }, { + name: "one", + n: "1", + want: "1", + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: "fffffffffffffffe0000000000000001", + }, { + name: "2^64", + n: "10000000000000000", + want: "100000000000000000000000000000000", + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: "fffffffffffffffffffffffffffffffe00000000000000000000000000000001", + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: "0", + }, { + name: "2^192 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "fffffffffffffffe000000000000000000000000000000000000000000000001", + }, { + name: "2^192", + n: "1000000000000000000000000000000000000000000000000", + want: "0", + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "1", + }, { + name: "alternating bits", + n: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "0d4985c1fe3a76b2ef2b67a3e01c5894d10d4985c1fe3a76b2ef2b67a3e01c59", + }, { + name: "alternating bits 2", + n: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "5894d10d4985c1fe3a76b2ef2b67a3e01c5894d10d4985c1fe3a76b2ef2b67a4", + }} + + for _, test := range tests { + n := hexToUint256(test.n) + want := hexToUint256(test.want) + + // Ensure squaring a value produces the expected result. + got := new(Uint256).SquareVal(n) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + + // Ensure self squaring also produces the expected result. + n.Square() + if !n.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256SquareRandom ensures that squaring uint256s created from random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestUint256SquareRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair. + bigN, n := randBigIntAndUint256(t, rng) + + // Calculate the square of the value using big ints. + bigIntResult := new(big.Int).Mul(bigN, bigN) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the square of the value using uint256s. + uint256Result := new(Uint256).SquareVal(n) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched square n: %x -- got %x, want %x", n, + bigIntResult, uint256Result) + } + } +} + +// TestUint256NumDigitsZero ensures that determining the number of digits for +// the value of zero returns zero digits. +func TestUint256NumDigitsZero(t *testing.T) { + t.Parallel() + + var zero Uint256 + if got := zero.numDigits(); got != 0 { + t.Fatalf("unexpected number of digits -- got %d, want %d", got, 0) + } +} + +// TestUint256Div ensures that dividing uint256s works as expected for edge +// cases. +func TestUint256Div(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded value + n2 string // second hex encoded value + want string // expected hex encoded value + }{{ + name: "0 / 1", + n1: "0", + n2: "1", + want: "0", + }, { + name: "1 / 1", + n1: "1", + n2: "1", + want: "1", + }, { + name: "(2^64 - 1) / (2^32 - 1) (divisor max uint32 < dividend max uint64)", + n1: "ffffffffffffffff", + n2: "ffffffff", + want: "100000001", + }, { + name: "(2^64 - 1) / (2^64 - 2) (dividend and divisor both uint64)", + n1: "ffffffffffffffff", + n2: "fffffffffffffffe", + want: "1", + }, { + name: "(2^64 - 1) / (2^64) (dividend max uint64 < divisor)", + n1: "ffffffffffffffff", + n2: "10000000000000000", + want: "0", + }, { + name: "(2^256 - 1) / (2^64 - 1) (divisor max uint64 < dividend)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffff", + want: "0000000000000001000000000000000100000000000000010000000000000001", + }, { + name: "Divisor scaling factor 0 (aka no normalization needed)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffff0000000000000000", + want: "0100000000000000010000000000000001", + }, { + name: "Divisor scaling factor 2^1 (with 2 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "7a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "0217a17a17a17a17a17a17a17a17a17a19", + }, { + name: "Divisor scaling factor 2^2 (with 2 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "3a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "046318c6318c6318c6318c6318c6318c69", + }, { + name: "Divisor scaling factor 2^3 (with 2 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "1a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "09b6db6db6db6db6db6db6db6db6db6dd8", + }, { + name: "Divisor scaling factor 2^4 (with 2 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "18ba2e8ba2e8ba2e8ba2e8ba2e8ba2e991", + }, { + name: "Divisor scaling factor 2^5 (with 3 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "075a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "22d0e5604189374bc6", + }, { + name: "Divisor scaling factor 2^6 (with 3 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "035a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "4c59d31674c59d3167", + }, { + name: "Divisor scaling factor 2^7 (with 3 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "015a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "bd37a6f4de9bd37a6f", + }, { + name: "Divisor scaling factor 2^8 (with 3 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "2d55555555555555555", + }, { + name: "Divisor scaling factor 2^60 (with 4 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "000000000000000fa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "105c64171905c641", + }, { + name: "Divisor scaling factor 2^61 (with 4 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0000000000000007a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "217a17a17a17a17a", + }, { + name: "Divisor scaling factor 2^62 (with 4 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0000000000000003a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "46318c6318c6318c", + }, { + name: "Divisor scaling factor 2^63 (max possible, with 4 words)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0000000000000001a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "9b6db6db6db6db6d", + }, { + name: "Divisor scaling factor 2^12", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "000fffffffffffff0000000000000000", + want: "100000000000010000000000001000000000", + }, { + name: "(2^256 - 1) / (2^256 - 1) (divisor == dividend with max uint256)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "1", + }, { + name: "(2^256 - 2) / (2^256 - 1) (divisor > dividend with max uint256)", + n1: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "0", + }, { + name: "one overestimate in full div", + n1: "00000000000000020000000000000000", + n2: "00000000000000010000000000000001", + want: "1", + }, { + name: "two overestimates in full div", + n1: "000000000000000180000000000000000000000000000000", + n2: "00000000000000020000000000000003", + want: "bffffffffffffffe", + }, { + name: "three overestimates in full div", + n1: "000000000000000400000000000000010000000000000000", + n2: "00000000000000020000000000000003", + want: "1fffffffffffffffd", + }, { + name: "four overestimates in full div", + n1: "0000000000000001c00000000000000000000000000000000000000000000000", + n2: "00000000000000020000000000000003", + want: "dffffffffffffffeb000000000000001", + }, { + name: "five overestimates in full div (max possible)", + n1: "4000000000000004000000000000000100000000000000010000000000000000", + n2: "00000000000000020000000000000003", + want: "2000000000000001cffffffffffffffdc800000000000003", + }, { + name: "place 2^128 estimate overflow min val (divisor digit == 2^63)", + n1: "000000000000000100000000000000000000000000000000", + n2: "000000000000000000000000000000010000000000000001", + want: "ffffffffffffffff", + }, { + name: "place 2^128 estimate overflow max instance (divisor digit == 2^64-1)", + n1: "fffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffff", + n2: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffff", + }, { + name: "place 2^128 estimate overflow plus correction", + n1: "000000000000001000000000000000000000000000000000", + n2: "000000000000000000000000000000100000000000000011", + want: "fffffffffffffffe", + }, { + name: "place 2^128 estimate overflow min val (divisor digit == 2^63)", + n1: "0000000000000001000000000000000000000000000000000000000000000000", + n2: "0000000000000000000000000000000100000000000000000000000000000001", + want: "ffffffffffffffff", + }, { + name: "place 2^192 estimate overflow max instance (divisor digit == 2^64-1)", + n1: "fffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffff", + n2: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffff", + }, { + name: "place 2^192 estimate overflow plus correction", + n1: "0000000000000001000000000000000000000000000000000000000000000000", + n2: "0000000000000000000000000000000100000000000000010000000000000010", + want: "fffffffffffffffe", + }, { + name: "1 by 1 word division", + n1: "9ec1968abe4be60c", + n2: "7882e0ef10ad6333", + want: "1", + }, { + name: "2 by 1 word division", + n1: "1ee7877f5f0d918d87ff2d8df5475a71", + n2: "3c7c5921ce59535e", + want: "82ccc7899eb1f7d3", + }, { + name: "3 by 1 word division", + n1: "2cafa3e891df1023a149b587a81fe1c6e29ef876d2f1076f", + n2: "6f69c5864e2b6302", + want: "66ad79286546f29cc022cd5621075865", + }, { + name: "4 by 1 word division", + n1: "859dcf75d21abb168921e000faaa91e0cc7f351857c054997f57935512a9acd3", + n2: "9fafdc72ff55ad30", + want: "d63495d2e66c2c6d9948d9be0c90a4cbf282d7f069f0d00b", + }, { + name: "2 by 2 word division", + n1: "b4bd27f8f94b274d0c720bef2840d2c8", + n2: "a51475e9b9fe383f275ee4130be38f05", + want: "1", + }, { + name: "3 by 2 word division", + n1: "d75621a1a66e56100a458d6798e45f73dffe19dc15dcf5e3", + n2: "fc8bc284d73ba47cc9f035ed496b6493", + want: "da4816fd39b9beb3", + }, { + name: "4 by 2 word division", + n1: "07c3dad55fdbcde468fe1012b9a870f45479dec3a856fe19466f7151636bab2e", + n2: "298eb48cf26c33ff40ff678fcaf66765", + want: "2fd57ae59007eb97d1506aff05fa6a0d", + }, { + name: "5 by 2 word division", + n1: "edce7b22ea206400183f1f3495ff19b401206f942ff0667fe038faae7086b750", + n2: "268b13d263e82dc7a2d5fa8e2ee6e85e", + want: "62b7be8d25b7099268b3cd12c9975f808", + }, { + name: "3 by 3 word division", + n1: "e266a16277f9d230fd6dbc29ebdd73c1bde3fc3c227bef9c", + n2: "8d1a0e0a050a5c3bcdba1ab9483bde24ee2d69c977076af0", + want: "1", + }, { + name: "4 by 3 word division", + n1: "e1543f55bf97d87f93cba9e0f33a12b1e3731236ad1558251ae5e2bed34c1517", + n2: "814e09a35d405b258b40a28558d2ed6e128ad239561e2b45", + want: "1be1c4b3fca685eb4", + }, { + name: "5 by 3 word division", + n1: "e4bdee26d527d18ebaaa10b4c0490ee22bdfa5f714ece496e99cb4247ed032c4", + n2: "2d34ea7ab3826dbca03bd4377fed82072a48c5743ec8369d", + want: "50f565bf6a8060a26", + }, { + name: "4 by 4 word division", + n1: "eed0d2d3ab6c94a36fa96d100c783928dffffff4f18e24720a3532c3466590cd", + n2: "db14897e65d547ace12e740764d0a9d0f237b4b13c033df350d6c7c21c4fa0ea", + want: "1", + }, { + name: "5 by 4 word division", + n1: "a879b1126365967b908c16e46f3a6332af5ca667fe92f2ae685fcf158da6f8d9", + n2: "2776bc3b312cdc32d506dc02a93d2dcd7de59c615451be57ae19d3c8ca5a9d59", + want: "4", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "1", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "0", + }} + + for _, test := range tests { + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure dividing two other values produces the expected result. + got := new(Uint256).Div2(n1, n2) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + + // Ensure single argument division also produces the expected result. + n1.Div(n2) + if !n1.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n1, + want) + continue + } + } +} + +// TestUint256DivRandom ensures that dividing two uint256s created from random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestUint256DivRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the quotient of the values using big ints. + bigIntResult := new(big.Int).Div(bigN1, bigN2) + + // Calculate the quotient of the values using uint256s. + uint256Result := new(Uint256).Div2(n1, n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched div n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256DivUint64 ensures that dividing a uint256 by a uint64 works as +// expected for edge cases. +func TestUint256DivUint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // hex encoded dividend + n2 uint64 // uint64 divisor + want string // expected hex encoded value + }{{ + name: "0 / 1", + n1: "0", + n2: 1, + want: "0", + }, { + name: "1 / 1", + n1: "1", + n2: 1, + want: "1", + }, { + name: "(2^64 - 1) / (2^32 - 1) (divisor max uint32 < dividend max uint64)", + n1: "ffffffffffffffff", + n2: 0xffffffff, + want: "100000001", + }, { + name: "(2^64 - 1) / (2^64 - 2) (dividend and divisor both uint64)", + n1: "ffffffffffffffff", + n2: 0xfffffffffffffffe, + want: "1", + }, { + name: "(2^256 - 1) / (2^64 - 1) (divisor max uint64 < dividend)", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: 0xffffffffffffffff, + want: "0000000000000001000000000000000100000000000000010000000000000001", + }, { + name: "1 by 1 word division", + n1: "9ec1968abe4be60c", + n2: 0x7882e0ef10ad6333, + want: "1", + }, { + name: "2 by 1 word division", + n1: "1ee7877f5f0d918d87ff2d8df5475a71", + n2: 0x3c7c5921ce59535e, + want: "82ccc7899eb1f7d3", + }, { + name: "3 by 1 word division", + n1: "2cafa3e891df1023a149b587a81fe1c6e29ef876d2f1076f", + n2: 0x6f69c5864e2b6302, + want: "66ad79286546f29cc022cd5621075865", + }, { + name: "4 by 1 word division", + n1: "859dcf75d21abb168921e000faaa91e0cc7f351857c054997f57935512a9acd3", + n2: 0x9fafdc72ff55ad30, + want: "d63495d2e66c2c6d9948d9be0c90a4cbf282d7f069f0d00b", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: 0x5a5a5a5a5a5a5a5a, + want: "1d5555555555555572aaaaaaaaaaaaaac8000000000000001", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: 0xa5a5a5a5a5a5a5a5, + want: "8ba2e8ba2e8ba2e945d1745d1745d174e8ba2e8ba2e8ba2f", + }} + + for _, test := range tests { + n1 := hexToUint256(test.n1) + want := hexToUint256(test.want) + + // Ensure single argument division also produces the expected result. + n1.DivUint64(test.n2) + if !n1.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n1, + want) + continue + } + } +} + +// TestUint256DivUint64Random ensures that dividing a uint256 by a uint64, both +// created from random values, works as expected by also performing the same +// operation with big ints and comparing the results. +func TestUint256DivUint64Random(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate two big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + n2 := rng.Uint64() + bigN2 := new(big.Int).SetUint64(n2) + + // Calculate the quotient of the values using big ints. + bigIntResult := new(big.Int).Div(bigN1, bigN2) + + // Calculate the quotient of the values using uint256s. + uint256Result := new(Uint256).Set(n1).DivUint64(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched div n1: %x, n2: %x -- got %x, want %x", n1, n2, + bigIntResult, uint256Result) + } + } +} + +// TestUint256DivByZeroPanic ensures division by zero results in a panic for the +// various division methods. +func TestUint256DivByZeroPanic(t *testing.T) { + t.Parallel() + + testPanic := func(fn func()) (paniced bool) { + // Setup a defer to catch the expected panic and update the return + // variable. + defer func() { + if err := recover(); err != nil { + paniced = true + } + }() + + fn() + return false + } + + // Ensure attempting to divide by zero via the two parameter variant panics. + paniced := testPanic(func() { + var n1, n2 Uint256 + _ = new(Uint256).Div2(&n1, &n2) + }) + if !paniced { + t.Fatal("Div2 did not panic on division by zero") + } + + // Ensure attempting to divide by zero via the single parameter variant + // panics. + paniced = testPanic(func() { + var n1, n2 Uint256 + n1.Div(&n2) + }) + if !paniced { + t.Fatal("Div did not panic on division by zero") + } + + // Ensure attempting to divide by zero via the uint64 parameter variant + // panics. + paniced = testPanic(func() { + var n1 Uint256 + n1.DivUint64(0) + }) + if !paniced { + t.Fatal("DivUint64 did not panic on division by zero") + } +} + +// TestUint256Negate ensures that negating uint256s mod 2^256 works as expected +// for edge cases. +func TestUint256Negate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + want string // expected hex encoded value + }{{ + name: "zero", + n: "0", + want: "0", + }, { + name: "one", + n: "1", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000001", + }, { + name: "2^64", + n: "10000000000000000", + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000", + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffff00000000000000000000000000000001", + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: "ffffffffffffffffffffffffffffffff00000000000000000000000000000000", + }, { + name: "2^192 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffff000000000000000000000000000000000000000000000001", + }, { + name: "2^192", + n: "1000000000000000000000000000000000000000000000000", + want: "ffffffffffffffff000000000000000000000000000000000000000000000000", + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "1", + }, { + name: "alternating bits", + n: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5b", + }, { + name: "alternating bits 2", + n: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a6", + }} + + for _, test := range tests { + n := hexToUint256(test.n) + want := hexToUint256(test.want) + + // Ensure negating a value produces the expected result. + got := new(Uint256).NegateVal(n) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + // Ensure self negation also produces the expected result. + n.Negate() + if !n.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256NegateRandom ensures that negating uint256s created from random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestUint256NegateRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair. + bigN, n := randBigIntAndUint256(t, rng) + + // Calculate the negation of the value using big ints. + bigIntResult := new(big.Int).Neg(bigN) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the negation of the value using uint256s. + uint256Result := new(Uint256).NegateVal(n) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched negate n: %x -- got %x, want %x", n, + bigIntResult, uint256Result) + } + } +} + +// TestUint256Lsh ensures that left shifting uint256s works as expected for edge +// cases. +func TestUint256Lsh(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + bits uint32 // number of bits to shift + want string // expected hex encoded value + }{{ + name: "zero << 0", + n: "0", + bits: 0, + want: "0", + }, { + name: "(2^256 - 1) << 0", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 0, + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) << 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 1, + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "(2^256 - 1) << 64", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 64, + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000", + }, { + name: "(2^256 - 1) << 66", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 66, + want: "fffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000", + }, { + name: "(2^256 - 1) << 128", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 128, + want: "ffffffffffffffffffffffffffffffff00000000000000000000000000000000", + }, { + name: "(2^256 - 1) << 130", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 130, + want: "fffffffffffffffffffffffffffffffc00000000000000000000000000000000", + }, { + name: "(2^256 - 1) << 192", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 192, + want: "ffffffffffffffff000000000000000000000000000000000000000000000000", + }, { + name: "alternating bits << 193", + n: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + bits: 193, + want: "4b4b4b4b4b4b4b4a000000000000000000000000000000000000000000000000", + }, { + name: "alternating bits 2 << 250", + n: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + bits: 250, + want: "6800000000000000000000000000000000000000000000000000000000000000", + }, { + name: "(2^256 - 1) << 256", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 256, + want: "0", + }, { + name: "(2^256 - 1) << 300", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 300, + want: "0", + }} + + for _, test := range tests { + n := hexToUint256(test.n) + want := hexToUint256(test.want) + + // Ensure left shifting the value produces the expected result. + got := new(Uint256).LshVal(n, test.bits) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + + // Ensure single argument left shifting produces the expected result. + n.Lsh(test.bits) + if !n.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256LshRandom ensures that left shifting uint256s created from random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestUint256LshRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair along with a random number of + // bits to shift it. + bigN, n := randBigIntAndUint256(t, rng) + randomShift := uint(rng.Int31n(300)) + + // Calculate the lsh of the value using big ints. + bigIntResult := new(big.Int).Lsh(bigN, randomShift) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the lsh of the value using uint256s. + uint256Result := new(Uint256).LshVal(n, uint32(randomShift)) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched lsh n: %x -- got %x, want %x", n, bigIntResult, + uint256Result) + } + } +} + +// TestUint256Rsh ensures that right shifting uint256s works as expected for +// edge cases. +func TestUint256Rsh(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + bits uint32 // number of bits to shift + want string // expected hex encoded value + }{{ + name: "zero >> 0", + n: "0", + bits: 0, + want: "0", + }, { + name: "(2^256 - 1) >> 0", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 0, + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 1, + want: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 64", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 64, + want: "0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 66", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 66, + want: "00000000000000003fffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 128", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 128, + want: "00000000000000000000000000000000ffffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 130", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 130, + want: "000000000000000000000000000000003fffffffffffffffffffffffffffffff", + }, { + name: "(2^256 - 1) >> 192", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 192, + want: "000000000000000000000000000000000000000000000000ffffffffffffffff", + }, { + name: "alternating bits >> 193", + n: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + bits: 193, + want: "00000000000000000000000000000000000000000000000052d2d2d2d2d2d2d2", + }, { + name: "alternating bits 2 >> 250", + n: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + bits: 250, + want: "0000000000000000000000000000000000000000000000000000000000000016", + }, { + name: "(2^256 - 1) >> 256", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 256, + want: "0", + }, { + name: "(2^256 - 1) >> 300", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + bits: 300, + want: "0", + }} + + for _, test := range tests { + n := hexToUint256(test.n) + want := hexToUint256(test.want) + + // Ensure right shifting the value produces the expected result. + got := new(Uint256).RshVal(n, test.bits) + if !got.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, got, + want) + continue + } + + // Ensure single argument right shifting produces the expected result. + n.Rsh(test.bits) + if !n.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256RshRandom ensures that right shifting uint256s created from random +// values works as expected by also performing the same operation with big ints +// and comparing the results. +func TestUint256RshRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair along with a random number of + // bits to shift it. + bigN, n := randBigIntAndUint256(t, rng) + randomShift := uint(rng.Int31n(300)) + + // Calculate the rsh of the value using big ints. + bigIntResult := new(big.Int).Rsh(bigN, randomShift) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the rsh of the value using uint256s. + uint256Result := new(Uint256).RshVal(n, uint32(randomShift)) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched rsh n: %x -- got %x, want %x", n, bigIntResult, + uint256Result) + } + } +} + +// TestUint256Not ensures that computing the bitwise not of uint256s works as +// expected for edge cases. +func TestUint256Not(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + want string // expected hex encoded value + }{{ + name: "zero", + n: "0", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "one", + n: "1", + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000", + }, { + name: "2^64", + n: "10000000000000000", + want: "fffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffff", + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffff00000000000000000000000000000000", + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: "fffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffff", + }, { + name: "2^192 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffff000000000000000000000000000000000000000000000000", + }, { + name: "2^192", + n: "1000000000000000000000000000000000000000000000000", + want: "fffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "0", + }, { + name: "alternating bits", + n: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + }, { + name: "alternating bits 2", + n: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + }} + + for _, test := range tests { + n := hexToUint256(test.n) + want := hexToUint256(test.want) + + // Ensure the bitwise not of the value produces the expected result. + n.Not() + if !n.Eq(want) { + t.Errorf("%q: wrong result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256NotRandom ensures that computing the bitwise not of uint256s +// created from random values works as expected by also performing the same +// operation with big ints and comparing the results. +func TestUint256NotRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + two256 := new(big.Int).Lsh(big.NewInt(1), 256) + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair. + bigN, n := randBigIntAndUint256(t, rng) + + // Calculate the bitwise not of the value using big ints. + bigIntResult := new(big.Int).Not(bigN) + bigIntResult.Mod(bigIntResult, two256) + + // Calculate the bitwise not of the value using uint256s. + uint256Result := new(Uint256).Set(n).Not() + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched not n: %x -- got %x, want %x", n, bigIntResult, + uint256Result) + } + } +} + +// TestUint256Or ensures that computing the bitwise or of two uint256s works +// as expected for edge cases. +func TestUint256Or(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 string // second hex encoded test value + want string // expected hex encoded result + }{{ + name: "0 | 0", + n1: "0", + n2: "0", + want: "0", + }, { + name: "2^256-1 | 0", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "0 | 2^256-1", + n1: "0", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^256-1 | 2^256-1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure bitwise or of the two values produces the expected result. + got := new(Uint256).Set(n1).Or(n2) + if !got.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + got, want) + continue + } + } +} + +// TestUint256OrRandom ensures that computing the bitwise or of uint256s created +// from random values works as expected by also performing the same operation +// with big ints and comparing the results. +func TestUint256OrRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the bitwise or of the values using big ints. + bigIntResult := new(big.Int).Or(bigN1, bigN2) + + // Calculate the bitwise or of the values using uint256s. + uint256Result := new(Uint256).Set(n1).Or(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched or n: %x -- got %x, want %x", n1, bigIntResult, + uint256Result) + } + } +} + +// TestUint256And ensures that computing the bitwise and of two uint256s works +// as expected for edge cases. +func TestUint256And(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 string // second hex encoded test value + want string // expected hex encoded result + }{{ + name: "0 & 0", + n1: "0", + n2: "0", + want: "0", + }, { + name: "2^256-1 & 0", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0", + want: "0", + }, { + name: "0 & 2^256-1", + n1: "0", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "0", + }, { + name: "2^256-1 & 2^256-1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "0", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "0", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure bitwise and of the two values produces the expected result. + got := new(Uint256).Set(n1).And(n2) + if !got.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + got, want) + continue + } + } +} + +// TestUint256AndRandom ensures that computing the bitwise and of uint256s +// created from random values works as expected by also performing the same +// operation with big ints and comparing the results. +func TestUint256AndRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the bitwise and of the values using big ints. + bigIntResult := new(big.Int).And(bigN1, bigN2) + + // Calculate the bitwise and of the values using uint256s. + uint256Result := new(Uint256).Set(n1).And(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched and n: %x -- got %x, want %x", n1, + bigIntResult, uint256Result) + } + } +} + +// TestUint256Xor ensures that computing the bitwise exclusive or of two +// uint256s works as expected for edge cases. +func TestUint256Xor(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n1 string // first hex encoded test value + n2 string // second hex encoded test value + want string // expected hex encoded result + }{{ + name: "0 xor 0", + n1: "0", + n2: "0", + want: "0", + }, { + name: "2^256-1 xor 0", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "0", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "0 xor 2^256-1", + n1: "0", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2^256-1 xor 2^256-1", + n1: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + n2: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "0", + }, { + name: "alternating bits", + n1: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + n2: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "alternating bits 2", + n1: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + n2: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }} + + for _, test := range tests { + // Parse test hex. + n1 := hexToUint256(test.n1) + n2 := hexToUint256(test.n2) + want := hexToUint256(test.want) + + // Ensure bitwise xor of the two values produces the expected result. + got := new(Uint256).Set(n1).Xor(n2) + if !got.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, + got, want) + continue + } + } +} + +// TestUint256XorRandom ensures that computing the bitwise exclusive or of +// uint256s created from random values works as expected by also performing the +// same operation with big ints and comparing the results. +func TestUint256XorRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pairs. + bigN1, n1 := randBigIntAndUint256(t, rng) + bigN2, n2 := randBigIntAndUint256(t, rng) + + // Calculate the bitwise exclusive or of the values using big ints. + bigIntResult := new(big.Int).Xor(bigN1, bigN2) + + // Calculate the bitwise exclusive or of the values using uint256s. + uint256Result := new(Uint256).Set(n1).Xor(n2) + + // Ensure they match. + bigIntResultHex := fmt.Sprintf("%064x", bigIntResult.Bytes()) + uint256ResultHex := fmt.Sprintf("%064x", uint256Result.Bytes()) + if bigIntResultHex != uint256ResultHex { + t.Fatalf("mismatched exclusive or n: %x -- got %x, want %x", n1, + bigIntResult, uint256Result) + } + } +} + +// TestUint256BitLen ensures determining the minimum number of bits required to +// represent a uint256 works as expected. +func TestUint256BitLen(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded value + want uint16 // expected result + }{{ + name: "zero", + n: "0", + want: 0, + }, { + name: "one", + n: "1", + want: 1, + }, { + name: "two", + n: "2", + want: 2, + }, { + name: "2^32 - 1", + n: "ffffffff", + want: 32, + }, { + name: "2^32", + n: "100000000", + want: 33, + }, { + name: "2^64 - 2", + n: "fffffffffffffffe", + want: 64, + }, { + name: "2^64 - 1", + n: "ffffffffffffffff", + want: 64, + }, { + name: "2^64", + n: "10000000000000000", + want: 65, + }, { + name: "2^128 - 1", + n: "ffffffffffffffffffffffffffffffff", + want: 128, + }, { + name: "2^128", + n: "100000000000000000000000000000000", + want: 129, + }, { + name: "2^192 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffff", + want: 192, + }, { + name: "2^192", + n: "1000000000000000000000000000000000000000000000000", + want: 193, + }, { + name: "2^255 - 1", + n: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: 255, + }, { + name: "2^256 - 1", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: 256, + }} + + for _, test := range tests { + got := hexToUint256(test.n).BitLen() + if got != test.want { + t.Errorf("%q: wrong result -- got: %v, want: %v", test.name, got, + test.want) + continue + } + } +} + +// TestUint256Text ensures the converting uint256s to the supported output bases +// via the Text method works as intended that that it also handles nil pointers +// as intended. +func TestUint256Text(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + base OutputBase // base to output + want string // expected output + }{{ + name: "binary", + n: "01abc", + base: OutputBaseBinary, + want: "1101010111100", + }, { + name: "octal", + n: "01abc", + base: OutputBaseOctal, + want: "15274", + }, { + name: "decimal", + n: "01abc", + base: OutputBaseDecimal, + want: "6844", + }, { + name: "hex", + n: "01abc", + base: OutputBaseHex, + want: "1abc", + }, { + name: "unsupported base", + n: "01abc", + base: OutputBase(100), + want: "base 100 not supported (Uint256=6844)", + }} + + var nNil *Uint256 + for _, test := range tests { + // Ensure nil pointers are handled as expected. + got := nNil.Text(test.base) + want := "" + if got != want { + t.Errorf("%q: unexpected nil result -- got: %s, want: %s", + test.name, got, want) + } + + // Parse test hex and ensure expected output for test output base. + n := hexToUint256(test.n) + got = n.Text(test.base) + if got != test.want { + t.Errorf("%q: unexpected result -- got: %s, want: %s", test.name, + got, test.want) + } + } +} + +// TestUint256Format ensures that formatting a uint256 via its fmt.Formatter +// works as intended including things such as the supported output bases, +// flags for alternate format (e.g. output bases, leading zeros), padding, and +// precision. +func TestUint256Format(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + n string // hex encoded test value + fmt string // format string + want string // expected output + }{{ + // --------------------------------------------------------------------- + // Zero for all supported bases with and without base prefix. + // --------------------------------------------------------------------- + + name: "0 binary", + n: "0", + fmt: "%b", + want: "0", + }, { + name: "0 binary with base prefix", + n: "0", + fmt: "%#b", + want: "0b0", + }, { + name: "0 octal", + n: "0", + fmt: "%o", + want: "0", + }, { + name: "0 octal with '0' base prefix", + n: "0", + fmt: "%#o", + want: "00", + }, { + name: "0 octal with '0o' base prefix", + n: "0", + fmt: "%O", + want: "0o0", + }, { + name: "0 decimal", + n: "0", + fmt: "%d", + want: "0", + }, { + name: "0 decimal with base prefix (no effect)", + n: "0", + fmt: "%#d", + want: "0", + }, { + name: "0 hex", + n: "0", + fmt: "%x", + want: "0", + }, { + name: "0 hex with lowercase base prefix", + n: "0", + fmt: "%#x", + want: "0x0", + }, { + name: "0 hex with uppercase base prefix", + n: "0", + fmt: "%#X", + want: "0X0", + }, { + // --------------------------------------------------------------------- + // Binary output for various values and base prefix combinations. + // --------------------------------------------------------------------- + + name: "2^8 - 1 binary", + n: "ff", + fmt: "%b", + want: "11111111", + }, { + name: "2^8 - 1 binary with base prefix", + n: "ff", + fmt: "%#b", + want: "0b11111111", + }, { + name: "2^16 - 1 binary", + n: "ffff", + fmt: "%b", + want: "1111111111111111", + }, { + name: "2^32 - 1 binary", + n: "ffffffff", + fmt: "%b", + want: "11111111111111111111111111111111", + }, { + name: "2^64 - 1 binary", + n: "ffffffffffffffff", + fmt: "%b", + want: "1111111111111111111111111111111111111111111111111111111111111111", + }, { + name: "2^128 - 1 binary", + n: "ffffffffffffffffffffffffffffffff", + fmt: "%b", + want: "1111111111111111111111111111111111111111111111111111111111111111" + + "1111111111111111111111111111111111111111111111111111111111111111", + }, { + name: "2^256 - 1 binary", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + fmt: "%b", + want: "1111111111111111111111111111111111111111111111111111111111111111" + + "1111111111111111111111111111111111111111111111111111111111111111" + + "1111111111111111111111111111111111111111111111111111111111111111" + + "1111111111111111111111111111111111111111111111111111111111111111", + }, { + // --------------------------------------------------------------------- + // Octal output for various values and base prefix combinations. + // --------------------------------------------------------------------- + + name: "2^8 - 1 octal", + n: "ff", + fmt: "%o", + want: "377", + }, { + name: "2^8 - 1 octal with '0' base prefix", + n: "ff", + fmt: "%#o", + want: "0377", + }, { + name: "2^16 - 1 octal", + n: "ffff", + fmt: "%o", + want: "177777", + }, { + name: "2^32 - 1 octal", + n: "ffffffff", + fmt: "%o", + want: "37777777777", + }, { + name: "2^64 - 1 octal with '0o' base prefix", + n: "ffffffffffffffff", + fmt: "%O", + want: "0o1777777777777777777777", + }, { + name: "2^128 - 1 octal", + n: "ffffffffffffffffffffffffffffffff", + fmt: "%o", + want: "3777777777777777777777777777777777777777777", + }, { + name: "2^256 - 1 octal", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + fmt: "%o", + want: "17777777777777777777777777777777777777777777777777777777777777" + + "777777777777777777777777", + }, { + // --------------------------------------------------------------------- + // Decimal output for various values via %d, %s, and %v. + // --------------------------------------------------------------------- + + name: "2^8 - 1 decimal", + n: "ff", + fmt: "%d", + want: "255", + }, { + name: "2^16 - 1 decimal", + n: "ffff", + fmt: "%d", + want: "65535", + }, { + name: "2^32 - 1 decimal", + n: "ffffffff", + fmt: "%d", + want: "4294967295", + }, { + name: "2^64 - 1 decimal", + n: "ffffffffffffffff", + fmt: "%d", + want: "18446744073709551615", + }, { + name: "2^128 - 1 decimal", + n: "ffffffffffffffffffffffffffffffff", + fmt: "%d", + want: "340282366920938463463374607431768211455", + }, { + name: "2^256 - 1 decimal", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + fmt: "%d", + want: "11579208923731619542357098500868790785326998466564056403945758" + + "4007913129639935", + }, { + name: "10^9 decimal via %s", + n: "3b9aca00", + fmt: "%s", + want: "1000000000", + }, { + name: "123456789 decimal via %v", + n: "75bcd15", + fmt: "%v", + want: "123456789", + }, { + // --------------------------------------------------------------------- + // Hex output for various values and base prefix combinations. + // --------------------------------------------------------------------- + + name: "2^8 - 1 hex", + n: "ff", + fmt: "%x", + want: "ff", + }, { + name: "2^8 - 1 hex with lowercase base prefix", + n: "ff", + fmt: "%#x", + want: "0xff", + }, { + name: "2^8 - 1 hex with uppercase base prefix", + n: "ff", + fmt: "%#X", + want: "0XFF", + }, { + name: "2^16 - 1 hex", + n: "ffff", + fmt: "%x", + want: "ffff", + }, { + name: "2^32 - 1 hex", + n: "ffffffff", + fmt: "%x", + want: "ffffffff", + }, { + name: "2^64 - 1 hex", + n: "ffffffffffffffff", + fmt: "%x", + want: "ffffffffffffffff", + }, { + name: "2^128 - 1 hex with lowercase base prefix", + n: "ffffffffffffffffffffffffffffffff", + fmt: "%#x", + want: "0xffffffffffffffffffffffffffffffff", + }, { + name: "2^256 - 1 hex", + n: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + fmt: "%x", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + // --------------------------------------------------------------------- + // Min precision with and without base prefix / zero pad flag. + // --------------------------------------------------------------------- + + name: "binary min 8 bits w/ val < 8 bits", + n: "a", + fmt: "%.8b", + want: "00001010", + }, { + name: "octal min 6 digits w/ val < 6 digits", + n: "4551", + fmt: "%.6o", + want: "042521", + }, { + name: "decimal min 5 digits w/ val < 5 digits", + n: "270f", + fmt: "%.5d", + want: "09999", + }, { + name: "hex min 12 digits w/ val < 12 digits", + n: "abcdef", + fmt: "%.12x", + want: "000000abcdef", + }, { + name: "binary min 8 bits w/ val < 8 bits zero pad", + n: "a", + fmt: "%0.8b", + want: "00001010", + }, { + name: "octal min 6 digits w/ val 6 digits zero pad", + n: "8555", + fmt: "%0.6o", + want: "102525", + }, { + name: "decimal min 5 digits w/ val 5 digits zero pad", + n: "2710", + fmt: "%0.5d", + want: "10000", + }, { + name: "hex min 12 digits w/ val 12 digits", + n: "abcdefabcdef", + fmt: "%.12x", + want: "abcdefabcdef", + }, { + name: "binary min 8 bits w/ val > 8 bits zero pad", + n: "100", + fmt: "%0.8b", + want: "100000000", + }, { + name: "octal min 6 digits w/ val > 6 digits", + n: "40000", + fmt: "%.6o", + want: "1000000", + }, { + name: "decimal min 5 digits w/ val > 5 digits", + n: "186a0", + fmt: "%.5d", + want: "100000", + }, { + name: "hex min 12 digits w/ val > 12 digits", + n: "1000000000000", + fmt: "%.12x", + want: "1000000000000", + }, { + name: "binary min 8 bits w/ val < 8 bits and base prefix", + n: "a", + fmt: "%#.8b", + want: "0b00001010", + }, { + name: "octal min 6 digits w/ val < 6 digits and '0' base prefix", + n: "4551", + fmt: "%#.6o", + want: "0042521", + }, { + name: "octal min 6 digits w/ val < 6 digits and '0o' base prefix", + n: "4551", + fmt: "%0.6O", + want: "0o042521", + }, { + name: "decimal min 5 digits w/ val < 5 digits and base prefix", + n: "270f", + fmt: "%#.5d", + want: "09999", + }, { + name: "hex min 12 digits w/ val < 12 digits and base prefix lowercase", + n: "abcdef", + fmt: "%#.12x", + want: "0x000000abcdef", + }, { + name: "hex min 12 digits w/ val < 12 digits and base prefix uppercase", + n: "abcdef", + fmt: "%#.12X", + want: "0X000000ABCDEF", + }, { + // --------------------------------------------------------------------- + // Min width with and without base prefix / zero pad flag. + // --------------------------------------------------------------------- + + name: "binary min width 8 w/ val < 8 bits zero pad", + n: "a", + fmt: "%08b", + want: "00001010", + }, { + name: "octal min width 6 w/ val < 6 digits zero pad", + n: "4551", + fmt: "%06o", + want: "042521", + }, { + name: "decimal min width 5 w/ val < 5 digits zero pad", + n: "270f", + fmt: "%05d", + want: "09999", + }, { + name: "hex min width 12 w/ val < 12 digits zero pad", + n: "abcdef", + fmt: "%012x", + want: "000000abcdef", + }, { + name: "binary min width 8 w/ val > 8 bits zero pad", + n: "100", + fmt: "%08b", + want: "100000000", + }, { + name: "octal min width 6 w/ val > 6 digits zero pad", + n: "40000", + fmt: "%06o", + want: "1000000", + }, { + name: "decimal min width 5 w/ val > 5 digits zero pad", + n: "186a0", + fmt: "%05d", + want: "100000", + }, { + name: "hex min width 12 w/ val > 12 digits zero pad", + n: "1000000000000", + fmt: "%012x", + want: "1000000000000", + }, { + name: "binary min width 8 w/ val < 8 bits left pad", + n: "a", + fmt: "%8b", + want: " 1010", + }, { + name: "octal min width 6 w/ val < 6 digits left pad", + n: "4551", + fmt: "%6o", + want: " 42521", + }, { + name: "decimal min width 5 w/ val < 5 digits left pad", + n: "270f", + fmt: "%5d", + want: " 9999", + }, { + name: "hex min width 12 w/ val < 12 digits left pad", + n: "abcdef", + fmt: "%12x", + want: " abcdef", + }, { + name: "binary min width 8 w/ val > 8 bits left pad", + n: "100", + fmt: "%8b", + want: "100000000", + }, { + name: "octal min width 6 w/ val > 6 digits left pad", + n: "40000", + fmt: "%6o", + want: "1000000", + }, { + name: "decimal min width 5 w/ val > 5 digits left pad", + n: "186a0", + fmt: "%5d", + want: "100000", + }, { + name: "hex min width 12 w/ val > 12 digits left pad", + n: "1000000000000", + fmt: "%12x", + want: "1000000000000", + }, { + name: "binary min width 8 w/ val < 8 bits right pad", + n: "a", + fmt: "%-8b", + want: "1010 ", + }, { + name: "octal min width 6 w/ val < 6 digits right pad", + n: "4551", + fmt: "%-6o", + want: "42521 ", + }, { + name: "decimal min width 5 w/ val < 5 digits right pad", + n: "270f", + fmt: "%-5d", + want: "9999 ", + }, { + name: "hex min width 12 w/ val < 12 digits right pad", + n: "abcdef", + fmt: "%-12x", + want: "abcdef ", + }, { + name: "binary min width 8 w/ val > 8 bits right pad", + n: "100", + fmt: "%-8b", + want: "100000000", + }, { + name: "octal min width 6 w/ val > 6 digits right pad", + n: "40000", + fmt: "%-6o", + want: "1000000", + }, { + name: "decimal min width 5 w/ val > 5 digits right pad", + n: "186a0", + fmt: "%-5d", + want: "100000", + }, { + name: "hex min width 12 w/ val > 12 digits right pad", + n: "1000000000000", + fmt: "%-12x", + want: "1000000000000", + }, { + name: "binary min width 8 w/ val < 8 bits and base prefix left pad", + n: "a", + fmt: "%#8b", + want: " 0b1010", + }, { + name: "octal min width 6 w/ val < 6 digits and base prefix left pad", + n: "4551", + fmt: "%#6o", + want: "042521", + }, { + name: "decimal min width 5 w/ val < 5 digits and base prefix left pad", + n: "270f", + fmt: "%#5d", + want: " 9999", + }, { + name: "hex min width 12 w/ val < 12 digits and base prefix left pad", + n: "abcdef", + fmt: "%#12x", + want: " 0xabcdef", + }, { + name: "binary min width 8 w/ val > 8 bits and base prefix left pad", + n: "100", + fmt: "%#8b", + want: "0b100000000", + }, { + name: "octal min width 6 w/ val > 6 digits and base prefix left pad", + n: "40000", + fmt: "%#6o", + want: "01000000", + }, { + name: "decimal min width 5 w/ val > 5 digits and base prefix left pad", + n: "186a0", + fmt: "%#5d", + want: "100000", + }, { + name: "hex min width 12 w/ val > 12 digits and base prefix left pad", + n: "1000000000000", + fmt: "%#12x", + want: "0x1000000000000", + }, { + name: "binary min width 8 w/ val < 8 bits and base prefix zero pad", + n: "a", + fmt: "%#08b", + want: "0b001010", + }, { + name: "octal min width 6 w/ val < 6 digits and base prefix zero pad", + n: "4551", + fmt: "%#06o", + want: "042521", + }, { + name: "decimal min width 5 w/ val < 5 digits and base prefix zero pad", + n: "270f", + fmt: "%#05d", + want: "09999", + }, { + name: "hex min width 12 w/ val < 12 digits and base prefix zero pad", + n: "abcdef", + fmt: "%#012x", + want: "0x0000abcdef", + }, { + // --------------------------------------------------------------------- + // Mixed min width and precision with and without base prefix. + // --------------------------------------------------------------------- + + name: "binary min width 8 min 6 bits w/ val 4 bits", + n: "9", + fmt: "%8.6b", + want: " 001001", + }, { + name: "octal min width 10 min 3 digits w/ val 2 digits and '0o' prefix", + n: "d", + fmt: "%10.3O", + want: " 0o015", + }, { + name: "decimal min width 12 min 7 digits w/ val 5 digits", + n: "c34f", + fmt: "%12.7d", + want: " 0049999", + }, { + name: "hex min width 32 min 16 digits w/ val 9 digits and base prefix", + n: "89abcdef0", + fmt: "%#32.16x", + want: " 0x000000089abcdef0", + }, { + // --------------------------------------------------------------------- + // Zero digits via precision with value == 0. + // --------------------------------------------------------------------- + + name: "binary min 0 implied digits w/ val == 0", + n: "0", + fmt: "%.b", + want: "", + }, { + name: "octal min 0 digits w/ val == 0", + n: "0", + fmt: "%.0o", + want: "", + }, { + name: "decimal min 0 digits w/ val == 0 zero pad", + n: "0", + fmt: "%0.0d", + want: "", + }, { + name: "hex min 0 digits w/ val == 0 and base prefix", + n: "0", + fmt: "%#.0x", + want: "", + }, { + // --------------------------------------------------------------------- + // Misc. + // --------------------------------------------------------------------- + + name: "unsupported format verb", + n: "1000", + fmt: "%f", + want: "%!f(Uint256=4096)", + }} + + for _, test := range tests { + // Parse test hex. + n := hexToUint256(test.n) + + got := fmt.Sprintf(test.fmt, n) + if got != test.want { + t.Errorf("%q: unexpected result -- got: %s, want: %s", test.name, + got, test.want) + } + } +} + +// TestUint256ToBigRandom ensures that converting uint256s created from random +// values to big ints works as expected. +func TestUint256ToBigRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair. + wantBig, n1 := randBigIntAndUint256(t, rng) + + // Convert to a big int and ensure they match. + bigIntResult := n1.ToBig() + if bigIntResult.Cmp(wantBig) != 0 { + t.Fatalf("mismatched big conversion: %x -- got %x, want %x", n1, + bigIntResult, wantBig) + } + + // Ensure setting an existing big int directly matches as well. + var bigIntResult2 big.Int + n1.PutBig(&bigIntResult2) + if bigIntResult2.Cmp(wantBig) != 0 { + t.Fatalf("mismatched big conversion: %x -- got %x, want %x", n1, + &bigIntResult2, wantBig) + } + } +} + +// TestUint256SetBig ensures that setting a uint256 to a standard library big +// integer works as expected for edge cases. +func TestUint256SetBig(t *testing.T) { + t.Parallel() + + tests := []struct { + name string // test description + in string // hex encoded big int test value + want string // expected hex encoded uint256 + }{{ + name: "0", + in: "00", + want: "0", + }, { + name: "1", + in: "1", + want: "1", + }, { + name: "-1", + in: "-1", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "2", + in: "2", + want: "2", + }, { + name: "-2", + in: "-2", + want: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, { + name: "max int256 (2^255 - 1)", + in: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "min int256 (-2^255)", + in: "-8000000000000000000000000000000000000000000000000000000000000000", + want: "8000000000000000000000000000000000000000000000000000000000000000", + }, { + name: "max uint256 (2^256 - 1)", + in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + want: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, { + name: "max uint256 + 1 (2^256)", + in: "10000000000000000000000000000000000000000000000000000000000000000", + want: "0", + }, { + name: "max uint256 + 2 (2^256 + 1)", + in: "10000000000000000000000000000000000000000000000000000000000000001", + want: "1", + }} + + for _, test := range tests { + // Parse test vals. + in, ok := new(big.Int).SetString(test.in, 16) + if !ok { + t.Errorf("%q: big int parse err for string %s", test.name, test.in) + continue + } + want := hexToUint256(test.want) + + // Ensure setting the uint256 to the test big int produces the expected + // value. + var n Uint256 + n.SetBig(in) + if !n.Eq(want) { + t.Errorf("%q: unexpected result -- got: %x, want: %x", test.name, n, + want) + continue + } + } +} + +// TestUint256SetBigRandom ensures that converting bit ints created from random +// values to uint256s works as expected. +func TestUint256SetBigRandom(t *testing.T) { + t.Parallel() + + // Use a unique random seed each test instance and log it if the tests fail. + seed := time.Now().Unix() + rng := rand.New(rand.NewSource(seed)) + defer func(t *testing.T, seed int64) { + if t.Failed() { + t.Logf("random seed: %d", seed) + } + }(t, seed) + + for i := 0; i < 100; i++ { + // Generate big integer and uint256 pair. + bigN, wantUint256 := randBigIntAndUint256(t, rng) + + // Convert the big int to a uint256 and ensure they match. + uint256Result := new(Uint256).SetBig(bigN) + if !uint256Result.Eq(wantUint256) { + t.Fatalf("mismatched uint256 conversion: %x -- got %x, want %x", + bigN, uint256Result, wantUint256) + } + } +}