uint256: Add general comparison support.

This adds the ability to compare a uint256 to another uint256 or a
uint64 along with associated tests to ensure proper functionality.

The comparison logic is the usual logic where -1 is returned when the
uint256 is less than the value being compared to, 0 when it is equal,
and 1 when it is greater.

This is part of a series of commits to fully implement the uint256
package.
This commit is contained in:
Dave Collins 2021-11-06 16:07:04 -05:00
parent e8ba80ed6a
commit 2d86902ba7
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
2 changed files with 82 additions and 11 deletions

View File

@ -19,16 +19,15 @@ var (
// callers may rely on "wrap around" semantics.
//
// It currently implements support for comparison operations (equals, less,
// greater), interpreting and producing big and little endian bytes, and other
// convenience methods such as whether or not the value can be represented as a
// uint64 without loss of precision.
// greater, cmp), interpreting and producing big and little endian bytes, and
// other convenience methods such as whether or not the value can be represented
// as a uint64 without loss of precision.
//
// Future commits will implement the primary arithmetic operations (addition,
// subtraction, multiplication, squaring, division, negation), bitwise
// operations (lsh, rsh, not, or, and, xor), comparison operations (cmp), and
// other convenience methods such as determining the minimum number of bits
// required to represent the current value and text formatting with base
// conversion.
// operations (lsh, rsh, not, or, and, xor), and other convenience methods such
// as determining the minimum number of bits required to represent the current
// value and text formatting with base conversion.
type Uint256 struct {
// The uint256 is represented as 4 unsigned 64-bit integers in base 2^64.
//
@ -422,3 +421,45 @@ func (n *Uint256) GtEq(n2 *Uint256) bool {
func (n *Uint256) GtEqUint64(n2 uint64) bool {
return !n.LtUint64(n2)
}
// Cmp compares the two uint256s and returns -1, 0, or 1 depending on whether
// the uint256 is less than, equal to, or greater than the given one.
//
// That is, it returns:
//
// -1 when n < n2
// 0 when n == n2
// +1 when n > n2
func (n *Uint256) Cmp(n2 *Uint256) int {
var borrow uint64
r0, borrow := bits.Sub64(n.n[0], n2.n[0], borrow)
r1, borrow := bits.Sub64(n.n[1], n2.n[1], borrow)
r2, borrow := bits.Sub64(n.n[2], n2.n[2], borrow)
r3, borrow := bits.Sub64(n.n[3], n2.n[3], borrow)
if borrow != 0 {
return -1
}
if r0|r1|r2|r3 == 0 {
return 0
}
return 1
}
// CmpUint64 compares the uint256 with the given uint64 and returns -1, 0, or 1
// depending on whether the uint256 is less than, equal to, or greater than the
// uint64.
//
// That is, it returns:
//
// -1 when n < n2
// 0 when n == n2
// +1 when n > n2
func (n *Uint256) CmpUint64(n2 uint64) int {
if n.LtUint64(n2) {
return -1
}
if n.GtUint64(n2) {
return 1
}
return 0
}

View File

@ -848,9 +848,17 @@ func TestUint256Comparison(t *testing.T) {
}}
for _, test := range tests {
// Ensure comparing the numbers produces the expected == result.
// 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 {
@ -924,8 +932,15 @@ func TestUint256ComparisonRandom(t *testing.T) {
t.Fatalf("failed equality check -- n2: %x", n2)
}
// Ensure comparing the numbers produces the expected == result.
// 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 {
@ -1060,8 +1075,16 @@ func TestUint256ComparisonUint64(t *testing.T) {
}}
for _, test := range tests {
// Ensure comparing the numbers produces the expected == result.
// 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 {
@ -1137,8 +1160,15 @@ func TestUint256ComparisonUint64Random(t *testing.T) {
t.Fatalf("failed equality check -- n: %x", truncatedN1)
}
// Ensure comparing the numbers produces the expected == result.
// 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 {