While the original plan was to keep uint256 as a part of the primitives module, there has been interest in using the uint256 code now and the primitives module will not be complete and ready for release for quite some time. Also, after further discussion, the general preference is for a separate module that lives at math/uint256 for this code since the primitives module will ultimately be squarely aimed at Decred-specific things while math/uint256 is more generally useful, including many projects outside of Decred as well. Thus, this moves the new uint256 package from the internal staging area to math/uint256 where it will serve as a base for a new math/uint256 module release. Finally, it updates all relevant paths and package README.md accordingly.
4238 lines
120 KiB
Go
4238 lines
120 KiB
Go
// 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"
|
|
"strings"
|
|
"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 := "<nil>"
|
|
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: "10^77 decimal via %s",
|
|
n: "dd15fe86affad91249ef0eb713f39ebeaa987b6e6fd2a0000000000000000000",
|
|
fmt: "%s",
|
|
want: "1" + strings.Repeat("0", 77),
|
|
}, {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestUint256FormatRandom ensures that the binary, octal, decimal, and hex
|
|
// output of uint256s created from random values works as expected by also
|
|
// performing the same operation with big ints and comparing the results.
|
|
func TestUint256FormatRandom(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.
|
|
bigN1, n1 := randBigIntAndUint256(t, rng)
|
|
|
|
// Ensure base 2 output matches.
|
|
bigIntBinary := bigN1.Text(2)
|
|
uint256Binary := n1.Text(OutputBaseBinary)
|
|
if bigIntBinary != uint256Binary {
|
|
t.Fatalf("mismatched binary n: %x -- got %s, want %s", bigN1,
|
|
bigIntBinary, uint256Binary)
|
|
}
|
|
|
|
// Ensure base 8 output matches.
|
|
bigIntOctal := bigN1.Text(8)
|
|
uint256Octal := n1.Text(OutputBaseOctal)
|
|
if bigIntOctal != uint256Octal {
|
|
t.Fatalf("mismatched octal n: %x -- got %s, want %s", bigN1,
|
|
bigIntOctal, uint256Octal)
|
|
}
|
|
|
|
// Ensure base 10 output matches.
|
|
bigIntDecimal := bigN1.Text(10)
|
|
uint256Decimal := n1.Text(OutputBaseDecimal)
|
|
if bigIntDecimal != uint256Decimal {
|
|
t.Fatalf("mismatched decimal n: %x -- got %s, want %s", bigN1,
|
|
bigIntDecimal, uint256Decimal)
|
|
}
|
|
|
|
// Ensure base 16 output matches.
|
|
bigIntHex := bigN1.Text(16)
|
|
uint256Hex := n1.Text(OutputBaseHex)
|
|
if bigIntHex != uint256Hex {
|
|
t.Fatalf("mismatched hex n: %x -- got %s, want %s", bigN1,
|
|
bigIntHex, uint256Hex)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
}
|