uint256: Add bitwise or support.

This adds support to compute the bitwise or of two uint256s along with
associated tests to ensure proper functionality.

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:33 -05:00
parent 3ce3e33056
commit 269eee2069
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
2 changed files with 109 additions and 2 deletions

View File

@ -24,12 +24,12 @@ var (
//
// It currently implements the primary arithmetic operations (addition,
// subtraction, multiplication, squaring, division, negation), bitwise
// operations (lsh, rsh, not), comparison operations (equals, less, greater,
// operations (lsh, rsh, not, or), comparison operations (equals, less, 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 bitwise operations (or, and, xor), and other
// Future commits will implement bitwise operations (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 {
@ -1287,3 +1287,16 @@ func (n *Uint256) Not() *Uint256 {
n.n[3] = ^n.n[3]
return n
}
// Or computes the bitwise or of the uint256 and the passed uint256 and stores
// the result in n.
//
// The uint256 is returned to support chaining. This enables syntax like:
// n.Or(n2).AddUint64(1) so that n = (n | n2) + 1.
func (n *Uint256) Or(n2 *Uint256) *Uint256 {
n.n[0] |= n2.n[0]
n.n[1] |= n2.n[1]
n.n[2] |= n2.n[2]
n.n[3] |= n2.n[3]
return n
}

View File

@ -3038,3 +3038,97 @@ func TestUint256NotRandom(t *testing.T) {
}
}
}
// 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)
}
}
}