Skip to content

Commit

Permalink
apd: add property based testing to assert that BigInt == big.Int
Browse files Browse the repository at this point in the history
This commit uses Golang's `testing/quick` package to exercise each of the
methods in the shared `BigInt` and `big.Int` interface and assert that
each implementation behaves identically for all inputs.

This testing caught a few minor issues around graceful handling of nil
receivers in unused methods. I confirmed that it would have been able
to find a more serious correctness issue with `BigInt.Sign` that I did
not catch until attempting to integrate the library with CockroachDB.
  • Loading branch information
nvanbenschoten committed Jan 6, 2022
1 parent cad88a2 commit e91e9ce
Show file tree
Hide file tree
Showing 3 changed files with 849 additions and 10 deletions.
16 changes: 11 additions & 5 deletions bigint.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,19 @@ func mulInline(xVal, yVal uint, xNeg, yNeg bool) (zVal uint, zNeg, ok bool) {

//gcassert:inline
func quoInline(xVal, yVal uint, xNeg, yNeg bool) (quoVal uint, quoNeg, ok bool) {
if yVal == 0 { // divide by 0
return 0, false, false
}
quo := xVal / yVal
neg := xNeg != yNeg
return quo, neg, true
}

//gcassert:inline
func remInline(xVal, yVal uint, xNeg, yNeg bool) (remVal uint, remNeg, ok bool) {
if yVal == 0 { // divide by 0
return 0, false, false
}
rem := xVal % yVal
return rem, xNeg, true
}
Expand Down Expand Up @@ -361,7 +367,7 @@ func (z *BigInt) AndNot(x, y *BigInt) *BigInt {
// Append calls (big.Int).Append.
func (z *BigInt) Append(buf []byte, base int) []byte {
var tmp1 big.Int
return z.inner(&tmp1).Append(buf, base)
return z.innerOrNil(&tmp1).Append(buf, base)
}

// Binomial calls (big.Int).Binomial.
Expand Down Expand Up @@ -770,10 +776,10 @@ func (z *BigInt) Set(x *BigInt) *BigInt {
}

// SetBit calls (big.Int).SetBit.
func (z *BigInt) SetBit(x *BigInt, i int, v uint) *BigInt {
func (z *BigInt) SetBit(x *BigInt, i int, b uint) *BigInt {
var tmp1, tmp2 big.Int
zi := z.inner(&tmp1)
zi.SetBit(x.inner(&tmp2), i, v)
zi.SetBit(x.inner(&tmp2), i, b)
z.updateInner(zi)
return z
}
Expand Down Expand Up @@ -863,7 +869,7 @@ func (z *BigInt) Sqrt(x *BigInt) *BigInt {
// String calls (big.Int).String.
func (z *BigInt) String() string {
var tmp1 big.Int
return z.inner(&tmp1).String()
return z.innerOrNil(&tmp1).String()
}

// Sub calls (big.Int).Sub.
Expand All @@ -886,7 +892,7 @@ func (z *BigInt) Sub(x, y *BigInt) *BigInt {
// Text calls (big.Int).Text.
func (z *BigInt) Text(base int) string {
var tmp1 big.Int
return z.inner(&tmp1).Text(base)
return z.innerOrNil(&tmp1).Text(base)
}

// TrailingZeroBits calls (big.Int).TrailingZeroBits.
Expand Down
23 changes: 18 additions & 5 deletions bigint_go1.15_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,29 @@

package apd

import "testing"
import (
"testing"
"testing/quick"
)

// TestBigIntMatchesMathBigInt15 is like TestBigIntMatchesMathBigInt, but for
// parts of the shared BigInt/big.Int API that were introduced in go1.15.
func TestBigIntMatchesMathBigInt15(t *testing.T) {
t.Run("FillBytes", func(t *testing.T) {
apd := func(z number) []byte {
return z.toApd(t).FillBytes(make([]byte, len(z)))
}
math := func(z number) []byte {
return z.toMath(t).FillBytes(make([]byte, len(z)))
}
require(t, quick.CheckEqual(apd, math, nil))
})
}

//////////////////////////////////////////////////////////////////////////////////
// The following tests were copied from the standard library's math/big package //
//////////////////////////////////////////////////////////////////////////////////

//
// Tests from src/math/big/int_test.go
//

func TestBigIntFillBytes(t *testing.T) {
checkResult := func(t *testing.T, buf []byte, want *BigInt) {
t.Helper()
Expand Down
Loading

0 comments on commit e91e9ce

Please sign in to comment.