// 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)
		}
	}
}
