go.tools/go/exact: serialization support and better unknown handling
- fixed various imprecise doc strings - consistent handling of unknown values R=adonovan CC=golang-dev https://golang.org/cl/41170043
This commit is contained in:
parent
4728c2f3f6
commit
e2828468ff
|
|
@ -3,7 +3,11 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package exact implements mathematically exact values
|
||||
// and operations for all Go basic types.
|
||||
// and operations for untyped Go constant values.
|
||||
//
|
||||
// A special Unknown value may be used when a constant
|
||||
// value is unknown due to an error; operations on unknown
|
||||
// values produce unknown values.
|
||||
//
|
||||
package exact
|
||||
|
||||
|
|
@ -132,15 +136,15 @@ func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
|
|||
// MakeFloat64 returns the numeric value for x.
|
||||
// If x is not finite, the result is unknown.
|
||||
func MakeFloat64(x float64) Value {
|
||||
f := new(big.Rat).SetFloat64(x)
|
||||
if f != nil {
|
||||
if f := new(big.Rat).SetFloat64(x); f != nil {
|
||||
return normFloat(f)
|
||||
}
|
||||
return unknownVal{}
|
||||
}
|
||||
|
||||
// MakeFromLiteral returns the corresponding literal value.
|
||||
// If the literal has illegal format, the result is nil.
|
||||
// MakeFromLiteral returns the corresponding integer, floating-point,
|
||||
// imaginary, character, or string value for a Go literal string. The
|
||||
// result is nil if the literal string is invalid.
|
||||
func MakeFromLiteral(lit string, tok token.Token) Value {
|
||||
switch tok {
|
||||
case token.INT:
|
||||
|
|
@ -176,15 +180,18 @@ func MakeFromLiteral(lit string, tok token.Token) Value {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(gri) should we instead a) return unknown, or b) an error?
|
||||
// TODO(gri) should we return an Unknown instead?
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Accessors
|
||||
//
|
||||
// For unknown arguments the result is the zero value for the respective
|
||||
// accessor type, except for Sign, where the result is 1.
|
||||
|
||||
// BoolVal returns the Go boolean value of x, which must be a Bool.
|
||||
// The result is false for unknown values.
|
||||
// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
|
||||
// If x is Unknown, the result is false.
|
||||
func BoolVal(x Value) bool {
|
||||
switch x := x.(type) {
|
||||
case boolVal:
|
||||
|
|
@ -192,11 +199,11 @@ func BoolVal(x Value) bool {
|
|||
case unknownVal:
|
||||
return false
|
||||
}
|
||||
panic(fmt.Sprintf("invalid BoolVal(%v)", x))
|
||||
panic(fmt.Sprintf("%v not a Bool", x))
|
||||
}
|
||||
|
||||
// StringVal returns the Go string value of x, which must be a String.
|
||||
// The result is "" for unknown values.
|
||||
// StringVal returns the Go string value of x, which must be a String or an Unknown.
|
||||
// If x is Unknown, the result is "".
|
||||
func StringVal(x Value) string {
|
||||
switch x := x.(type) {
|
||||
case stringVal:
|
||||
|
|
@ -204,12 +211,12 @@ func StringVal(x Value) string {
|
|||
case unknownVal:
|
||||
return ""
|
||||
}
|
||||
panic(fmt.Sprintf("invalidStringVal(%v)", x))
|
||||
panic(fmt.Sprintf("%v not a String", x))
|
||||
}
|
||||
|
||||
// Int64Val returns the Go int64 value of x and whether the result is exact;
|
||||
// x must be an Int. If the result is not exact, its value is undefined.
|
||||
// The result is (0, false) for unknown values.
|
||||
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Int64Val(x Value) (int64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
|
|
@ -219,12 +226,12 @@ func Int64Val(x Value) (int64, bool) {
|
|||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("invalid Int64Val(%v)", x))
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Uint64Val returns the Go uint64 value of x and whether the result is exact;
|
||||
// x must be an Int. If the result is not exact, its value is undefined.
|
||||
// The result is (0, false) for unknown values.
|
||||
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Uint64Val(x Value) (uint64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
|
|
@ -234,11 +241,12 @@ func Uint64Val(x Value) (uint64, bool) {
|
|||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("invalid Uint64Val(%v)", x))
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
|
||||
// x must be numeric but not Complex. The result is (0, false) for unknown values.
|
||||
// x must be numeric but not Complex, or Unknown.
|
||||
// If x is Unknown, the result is (0, false).
|
||||
func Float64Val(x Value) (float64, bool) {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
|
|
@ -251,12 +259,12 @@ func Float64Val(x Value) (float64, bool) {
|
|||
case unknownVal:
|
||||
return 0, false
|
||||
}
|
||||
panic(fmt.Sprintf("invalid Float64Val(%v)", x))
|
||||
panic(fmt.Sprintf("%v not a Float", x))
|
||||
}
|
||||
|
||||
// BitLen() returns the number of bits required to represent
|
||||
// the absolute value x in binary representation; x must be an Int.
|
||||
// The result is 0 for unknown values.
|
||||
// BitLen returns the number of bits required to represent
|
||||
// the absolute value x in binary representation; x must be an Int or an Unknown.
|
||||
// If x is Unknown, the result is 0.
|
||||
func BitLen(x Value) int {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
|
|
@ -266,13 +274,12 @@ func BitLen(x Value) int {
|
|||
case unknownVal:
|
||||
return 0
|
||||
}
|
||||
panic(fmt.Sprintf("invalid BitLen(%v)", x))
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
// Sign returns -1, 0, or 1 depending on whether
|
||||
// x < 0, x == 0, or x > 0. For complex values z,
|
||||
// the sign is 0 if z == 0, otherwise it is != 0.
|
||||
// The result is 1 for unknown values.
|
||||
// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
|
||||
// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
|
||||
// otherwise it is != 0. If x is Unknown, the result is 1.
|
||||
func Sign(x Value) int {
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
|
|
@ -292,50 +299,159 @@ func Sign(x Value) int {
|
|||
case unknownVal:
|
||||
return 1 // avoid spurious division by zero errors
|
||||
}
|
||||
panic(fmt.Sprintf("invalid Sign(%v)", x))
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for serializing/deserializing integers
|
||||
|
||||
const (
|
||||
// Compute the size of a Word in bytes.
|
||||
_m = ^big.Word(0)
|
||||
_log = _m>>8&1 + _m>>16&1 + _m>>32&1
|
||||
wordSize = 1 << _log
|
||||
)
|
||||
|
||||
// Bytes returns the bytes for the absolute value of x in little-
|
||||
// endian binary representation; x must be an Int.
|
||||
func Bytes(x Value) []byte {
|
||||
var val *big.Int
|
||||
switch x := x.(type) {
|
||||
case int64Val:
|
||||
val = new(big.Int).SetInt64(int64(x))
|
||||
case intVal:
|
||||
val = x.val
|
||||
default:
|
||||
panic(fmt.Sprintf("%v not an Int", x))
|
||||
}
|
||||
|
||||
words := val.Bits()
|
||||
bytes := make([]byte, len(words)*wordSize)
|
||||
|
||||
i := 0
|
||||
for _, w := range words {
|
||||
for j := 0; j < wordSize; j++ {
|
||||
bytes[i] = byte(w)
|
||||
w >>= 8
|
||||
i++
|
||||
}
|
||||
}
|
||||
// remove leading 0's
|
||||
for i > 0 && bytes[i-1] == 0 {
|
||||
i--
|
||||
}
|
||||
|
||||
return bytes[:i]
|
||||
}
|
||||
|
||||
// MakeFromBytes returns the Int value given the bytes of its little-endian
|
||||
// binary representation. An empty byte slice argument represents 0.
|
||||
func MakeFromBytes(bytes []byte) Value {
|
||||
words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
|
||||
|
||||
i := 0
|
||||
var w big.Word
|
||||
var s uint
|
||||
for _, b := range bytes {
|
||||
w |= big.Word(b) << s
|
||||
if s += 8; s == wordSize*8 {
|
||||
words[i] = w
|
||||
i++
|
||||
w = 0
|
||||
s = 0
|
||||
}
|
||||
}
|
||||
// store last word
|
||||
if i < len(words) {
|
||||
words[i] = w
|
||||
i++
|
||||
}
|
||||
// remove leading 0's
|
||||
for i > 0 && words[i-1] == 0 {
|
||||
i--
|
||||
}
|
||||
|
||||
return normInt(new(big.Int).SetBits(words[:i]))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for disassembling fractions
|
||||
|
||||
// Num returns the numerator of x; x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown, otherwise it is an Int.
|
||||
func Num(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal, int64Val, intVal:
|
||||
return x
|
||||
case floatVal:
|
||||
return normInt(x.val.Num())
|
||||
}
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
|
||||
// Denom returns the denominator of x; x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
|
||||
func Denom(x Value) Value {
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val, intVal:
|
||||
return int64Val(1)
|
||||
case floatVal:
|
||||
return normInt(x.val.Denom())
|
||||
}
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support for assembling/disassembling complex numbers
|
||||
|
||||
// MakeImag returns the numeric value x*i (possibly 0);
|
||||
// x must be numeric but not Complex.
|
||||
// The result is unknown for unknown values.
|
||||
// x must be Int, Float, or Unknown.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func MakeImag(x Value) Value {
|
||||
var im *big.Rat
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val:
|
||||
im = big.NewRat(int64(x), 1)
|
||||
case intVal:
|
||||
im = new(big.Rat).SetFrac(x.val, int1)
|
||||
case floatVal:
|
||||
im = x.val
|
||||
case unknownVal:
|
||||
return x
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid MakeImag(%v)", x))
|
||||
panic(fmt.Sprintf("%v not Int or Float", x))
|
||||
}
|
||||
return normComplex(rat0, im)
|
||||
}
|
||||
|
||||
// Real returns the real part of x, which must be a numeric value.
|
||||
// The result is unknown for unknown values.
|
||||
// Real returns the real part of x, which must be a numeric or unknown value.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func Real(x Value) Value {
|
||||
if z, ok := x.(complexVal); ok {
|
||||
return normFloat(z.re)
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val, intVal, floatVal:
|
||||
return int64Val(0)
|
||||
case complexVal:
|
||||
return normFloat(x.re)
|
||||
}
|
||||
// TODO(gri) should we check explicit for unknownVal and disallow all others?
|
||||
return x
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// Imag returns the imaginary part of x, which must be a numeric value.
|
||||
// The result is 0 for unknown values.
|
||||
// Imag returns the imaginary part of x, which must be a numeric or unknown value.
|
||||
// If x is Unknown, the result is Unknown.
|
||||
func Imag(x Value) Value {
|
||||
if z, ok := x.(complexVal); ok {
|
||||
return normFloat(z.im)
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return x
|
||||
case int64Val, intVal, floatVal:
|
||||
return int64Val(0)
|
||||
case complexVal:
|
||||
return normFloat(x.im)
|
||||
}
|
||||
// TODO(gri) should we check explicit for unknownVal and disallow all others?
|
||||
return int64Val(0)
|
||||
panic(fmt.Sprintf("%v not numeric", x))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -356,6 +472,7 @@ func is63bit(x int64) bool {
|
|||
// UnaryOp returns the result of the unary expression op y.
|
||||
// The operation must be defined for the operand.
|
||||
// If size >= 0 it specifies the ^ (xor) result size in bytes.
|
||||
// If y is Unknown, the result is Unknown.
|
||||
//
|
||||
func UnaryOp(op token.Token, y Value, size int) Value {
|
||||
switch op {
|
||||
|
|
@ -367,6 +484,8 @@ func UnaryOp(op token.Token, y Value, size int) Value {
|
|||
|
||||
case token.SUB:
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case int64Val:
|
||||
if z := -y; z != y {
|
||||
return z // no overflow
|
||||
|
|
@ -383,6 +502,8 @@ func UnaryOp(op token.Token, y Value, size int) Value {
|
|||
case token.XOR:
|
||||
var z big.Int
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case int64Val:
|
||||
z.Not(big.NewInt(int64(y)))
|
||||
case intVal:
|
||||
|
|
@ -400,7 +521,12 @@ func UnaryOp(op token.Token, y Value, size int) Value {
|
|||
return normInt(&z)
|
||||
|
||||
case token.NOT:
|
||||
return !y.(boolVal)
|
||||
switch y := y.(type) {
|
||||
case unknownVal:
|
||||
return y
|
||||
case boolVal:
|
||||
return !y
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
|
|
@ -429,7 +555,8 @@ func ord(x Value) int {
|
|||
|
||||
// match returns the matching representation (same type) with the
|
||||
// smallest complexity for two values x and y. If one of them is
|
||||
// numeric, both of them must be numeric.
|
||||
// numeric, both of them must be numeric. If one of them is Unknown,
|
||||
// both results are Unknown.
|
||||
//
|
||||
func match(x, y Value) (_, _ Value) {
|
||||
if ord(x) > ord(y) {
|
||||
|
|
@ -439,7 +566,10 @@ func match(x, y Value) (_, _ Value) {
|
|||
// ord(x) <= ord(y)
|
||||
|
||||
switch x := x.(type) {
|
||||
case unknownVal, boolVal, stringVal, complexVal:
|
||||
case unknownVal:
|
||||
return x, x
|
||||
|
||||
case boolVal, stringVal, complexVal:
|
||||
return x, y
|
||||
|
||||
case int64Val:
|
||||
|
|
@ -477,7 +607,8 @@ func match(x, y Value) (_, _ Value) {
|
|||
}
|
||||
|
||||
// BinaryOp returns the result of the binary expression x op y.
|
||||
// The operation must be defined for the operands.
|
||||
// The operation must be defined for the operands. If one of the
|
||||
// operands is Unknown, the result is Unknown.
|
||||
// To force integer division of Int operands, use op == token.QUO_ASSIGN
|
||||
// instead of token.QUO; the result is guaranteed to be Int in this case.
|
||||
// Division by zero leads to a run-time panic.
|
||||
|
|
@ -639,7 +770,7 @@ Error:
|
|||
|
||||
// Shift returns the result of the shift expression x op s
|
||||
// with op == token.SHL or token.SHR (<< or >>). x must be
|
||||
// an Int.
|
||||
// an Int or an Unknown. If x is Unknown, the result is x.
|
||||
//
|
||||
func Shift(x Value, op token.Token, s uint) Value {
|
||||
switch x := x.(type) {
|
||||
|
|
@ -694,13 +825,15 @@ func cmpZero(x int, op token.Token) bool {
|
|||
|
||||
// Compare returns the result of the comparison x op y.
|
||||
// The comparison must be defined for the operands.
|
||||
// If one of the operands is Unknown, the result is
|
||||
// false.
|
||||
//
|
||||
func Compare(x Value, op token.Token, y Value) bool {
|
||||
x, y = match(x, y)
|
||||
|
||||
switch x := x.(type) {
|
||||
case unknownVal:
|
||||
return true
|
||||
return false
|
||||
|
||||
case boolVal:
|
||||
y := y.(boolVal)
|
||||
|
|
|
|||
|
|
@ -12,14 +12,19 @@ import (
|
|||
|
||||
// TODO(gri) expand this test framework
|
||||
|
||||
var tests = []string{
|
||||
var opTests = []string{
|
||||
// unary operations
|
||||
`+ 0 = 0`,
|
||||
`+ ? = ?`,
|
||||
`- 1 = -1`,
|
||||
`- ? = ?`,
|
||||
`^ 0 = -1`,
|
||||
`^ ? = ?`,
|
||||
|
||||
`! true = false`,
|
||||
`! false = true`,
|
||||
`! ? = ?`,
|
||||
|
||||
// etc.
|
||||
|
||||
// binary operations
|
||||
|
|
@ -33,35 +38,51 @@ var tests = []string{
|
|||
`0 + 0.1i = 0.1i`,
|
||||
`0.1 + 0.9 = 1`,
|
||||
`1e100 + 1e100 = 2e100`,
|
||||
`? + 0 = ?`,
|
||||
`0 + ? = ?`,
|
||||
|
||||
`0 - 0 = 0`,
|
||||
`0 - 0.1 = -0.1`,
|
||||
`0 - 0.1i = -0.1i`,
|
||||
`1e100 - 1e100 = 0`,
|
||||
`? - 0 = ?`,
|
||||
`0 - ? = ?`,
|
||||
|
||||
`0 * 0 = 0`,
|
||||
`1 * 0.1 = 0.1`,
|
||||
`1 * 0.1i = 0.1i`,
|
||||
`1i * 1i = -1`,
|
||||
`? * 0 = ?`,
|
||||
`0 * ? = ?`,
|
||||
|
||||
`0 / 0 = "division_by_zero"`,
|
||||
`10 / 2 = 5`,
|
||||
`5 / 3 = 5/3`,
|
||||
`5i / 3i = 5/3`,
|
||||
`? / 0 = ?`,
|
||||
`0 / ? = ?`,
|
||||
|
||||
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
|
||||
`10 % 3 = 1`,
|
||||
`? % 0 = ?`,
|
||||
`0 % ? = ?`,
|
||||
|
||||
`0 & 0 = 0`,
|
||||
`12345 & 0 = 0`,
|
||||
`0xff & 0xf = 0xf`,
|
||||
`? & 0 = ?`,
|
||||
`0 & ? = ?`,
|
||||
|
||||
`0 | 0 = 0`,
|
||||
`12345 | 0 = 12345`,
|
||||
`0xb | 0xa0 = 0xab`,
|
||||
`? | 0 = ?`,
|
||||
`0 | ? = ?`,
|
||||
|
||||
`0 ^ 0 = 0`,
|
||||
`1 ^ -1 = -2`,
|
||||
`? ^ 0 = ?`,
|
||||
`0 ^ ? = ?`,
|
||||
|
||||
`0 &^ 0 = 0`,
|
||||
`0xf &^ 1 = 0xe`,
|
||||
|
|
@ -73,6 +94,8 @@ var tests = []string{
|
|||
`1 << 10 = 1024`,
|
||||
`0 >> 0 = 0`,
|
||||
`1024 >> 10 == 1`,
|
||||
`? << 0 == ?`,
|
||||
`? >> 10 == ?`,
|
||||
// etc.
|
||||
|
||||
// comparisons
|
||||
|
|
@ -106,11 +129,26 @@ var tests = []string{
|
|||
`1/123456788 <= 1/123456789 == false`,
|
||||
`0.11 > 0.11 = false`,
|
||||
`0.11 >= 0.11 = true`,
|
||||
|
||||
`? == 0 = false`,
|
||||
`? != 0 = false`,
|
||||
`? < 10 = false`,
|
||||
`? <= 10 = false`,
|
||||
`? > 10 = false`,
|
||||
`? >= 10 = false`,
|
||||
|
||||
`0 == ? = false`,
|
||||
`0 != ? = false`,
|
||||
`0 < ? = false`,
|
||||
`10 <= ? = false`,
|
||||
`0 > ? = false`,
|
||||
`10 >= ? = false`,
|
||||
|
||||
// etc.
|
||||
}
|
||||
|
||||
func TestOps(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
for _, test := range opTests {
|
||||
a := strings.Split(test, " ")
|
||||
i := 0 // operator index
|
||||
|
||||
|
|
@ -136,18 +174,27 @@ func TestOps(t *testing.T) {
|
|||
|
||||
got := doOp(x, op, y)
|
||||
want := val(a[i+3])
|
||||
if !Compare(got, token.EQL, want) {
|
||||
if !eql(got, want) {
|
||||
t.Errorf("%s: got %s; want %s", test, got, want)
|
||||
}
|
||||
if x0 != nil && !Compare(x, token.EQL, x0) {
|
||||
if x0 != nil && !eql(x, x0) {
|
||||
t.Errorf("%s: x changed to %s", test, x)
|
||||
}
|
||||
if !Compare(y, token.EQL, y0) {
|
||||
if !eql(y, y0) {
|
||||
t.Errorf("%s: y changed to %s", test, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func eql(x, y Value) bool {
|
||||
_, ux := x.(unknownVal)
|
||||
_, uy := y.(unknownVal)
|
||||
if ux || uy {
|
||||
return ux == uy
|
||||
}
|
||||
return Compare(x, token.EQL, y)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Support functions
|
||||
|
||||
|
|
@ -238,3 +285,64 @@ func doOp(x Value, op token.Token, y Value) (z Value) {
|
|||
return BinaryOp(x, op, y)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Other tests
|
||||
|
||||
var fracTests = []string{
|
||||
"0 0 1",
|
||||
"1 1 1",
|
||||
"-1 -1 1",
|
||||
"1.2 6 5",
|
||||
"-0.991 -991 1000",
|
||||
"1e100 1e100 1",
|
||||
}
|
||||
|
||||
func TestFractions(t *testing.T) {
|
||||
for _, test := range fracTests {
|
||||
a := strings.Split(test, " ")
|
||||
if len(a) != 3 {
|
||||
t.Errorf("invalid test case: %s", test)
|
||||
continue
|
||||
}
|
||||
|
||||
x := val(a[0])
|
||||
n := val(a[1])
|
||||
d := val(a[2])
|
||||
|
||||
if got := Num(x); !eql(got, n) {
|
||||
t.Errorf("%s: got num = %s; want %s", test, got, n)
|
||||
}
|
||||
|
||||
if got := Denom(x); !eql(got, d) {
|
||||
t.Errorf("%s: got denom = %s; want %s", test, got, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bytesTests = []string{
|
||||
"0",
|
||||
"1",
|
||||
"123456789",
|
||||
"123456789012345678901234567890123456789012345678901234567890",
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
for _, test := range bytesTests {
|
||||
x := val(test)
|
||||
bytes := Bytes(x)
|
||||
|
||||
// special case 0
|
||||
if Sign(x) == 0 && len(bytes) != 0 {
|
||||
t.Errorf("%s: got %v; want empty byte slice", test, bytes)
|
||||
}
|
||||
|
||||
if n := len(bytes); n > 0 && bytes[n-1] == 0 {
|
||||
t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
|
||||
}
|
||||
|
||||
if got := MakeFromBytes(bytes); !eql(got, x) {
|
||||
t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue