4238 lines
120 KiB
Go
4238 lines
120 KiB
Go
// Copyright (c) 2021-2023 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 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)
|
|
}
|
|
}
|
|
}
|