diff options
| author | Shulhan <ms@kilabit.info> | 2021-11-18 00:08:46 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2021-11-19 10:49:08 +0700 |
| commit | f542979c53b8f16b6a9904e6ea5b30963ef88852 (patch) | |
| tree | 5d7dfc61ff6aa5298fef2aeab249ddb7ebbb926d | |
| parent | ea0846a642f7a8bc0d07e776ff9ca64f9f27992d (diff) | |
| download | pakakeh.go-f542979c53b8f16b6a9904e6ea5b30963ef88852.tar.xz | |
math/big: refactoring the Rat
The promise of this package is to provide the Rat type with RoundToZero
and without panic.
In order to do that we remove the MustRat() function and check for nil
on pointer receiver before doing operation, and check for zero value
when doing quotation.
Another breaking changes are,
* Humanize() with nil pointer receiver will return string "0"
* Any IsXxx() methods with nil pointer receiver will return false
* MarshalJSON() will nil pointer receiver will return "null" instead of
"0" now
| -rw-r--r-- | lib/math/big/rat.go | 230 | ||||
| -rw-r--r-- | lib/math/big/rat_example_test.go | 575 | ||||
| -rw-r--r-- | lib/math/big/rat_test.go | 795 |
3 files changed, 723 insertions, 877 deletions
diff --git a/lib/math/big/rat.go b/lib/math/big/rat.go index c7158210..58cae2cf 100644 --- a/lib/math/big/rat.go +++ b/lib/math/big/rat.go @@ -8,7 +8,6 @@ import ( "bytes" "database/sql/driver" "fmt" - "log" "math/big" "strconv" "strings" @@ -19,26 +18,27 @@ import ( var ratZero = NewRat(0) // -// Rat extend the standard big.Rat using rounding mode ToZero. +// Rat extend the standard big.Rat using rounding mode ToZero and without +// panic. // type Rat struct { big.Rat } // -// AddRat return the sum of `f+g+...`. -// It will return nil if `f` or `g` is not convertable to Rat. +// AddRat return the sum of `f[0]+f[1]+...`. +// It will return nil if the first parameter is not convertable to Rat. // func AddRat(f ...interface{}) *Rat { if len(f) == 0 { return nil } - total := NewRat(f[0]) + total := toRat(f[0]) if total == nil { return nil } for x := 1; x < len(f); x++ { - rx := toRat(f[x], nil) + rx := toRat(f[x]) if rx == nil { continue } @@ -48,35 +48,31 @@ func AddRat(f ...interface{}) *Rat { } // -// NewRat create and initialize new Rat value from v or nil if v is invalid -// type that cannot be converted to Rat. +// NewRat create and initialize new Rat value from v. +// It will return nil if v is not convertable to Rat. +// +// Empty string or empty []byte still considered as valid, and it will return +// it as zero. // func NewRat(v interface{}) (r *Rat) { - r = &Rat{} - - got := toRat(v, r) - if got == nil { - return nil - } - - return r + return toRat(v) } // -// MulRat return the result of multiplication `f*...`. -// It will return nil if parameter is empty or `f` is not convertable to Rat. +// MulRat return the result of multiplication `f[0]*f[1]*...`. +// It will return nil if the first parameter is not convertable to Rat. // func MulRat(f ...interface{}) *Rat { if len(f) == 0 { return nil } - total := NewRat(f[0]) + total := toRat(f[0]) if total == nil { // Its equal to `0*...` return nil } for x := 1; x < len(f); x++ { - rx := toRat(f[x], nil) + rx := toRat(f[x]) if rx == nil { continue } @@ -86,19 +82,8 @@ func MulRat(f ...interface{}) *Rat { } // -// MustRat create and initialize new Rat value from v or panic if v is -// unknown type that cannot be converted to Rat. -// -func MustRat(v interface{}) (r *Rat) { - r = NewRat(v) - if r == nil { - log.Fatalf("MustRat: cannot convert %v to Rat", v) - } - return r -} - -// -// QuoRat return the quotient of `f/g/...` as new Rat. +// QuoRat return the quotient of `f[0]/f[1]/...` as new Rat. +// It will return nil if the first parameter is not convertable to Rat. // If the second or rest of parameters can not be converted to Rat or zero it // will return nil instead of panic. // @@ -106,7 +91,7 @@ func QuoRat(f ...interface{}) *Rat { if len(f) == 0 { return nil } - total := NewRat(f[0]) + total := toRat(f[0]) if total == nil { return nil } @@ -121,18 +106,19 @@ func QuoRat(f ...interface{}) *Rat { } // -// SubRat return the result of subtraction `f-g-...` as new Rat. +// SubRat return the result of subtraction `f[0]-f[1]-...` as new Rat. +// It will return nil if the first parameter is not convertable to Rat. // func SubRat(f ...interface{}) *Rat { if len(f) == 0 { return nil } - total := NewRat(f[0]) + total := toRat(f[0]) if total == nil { return nil } for x := 1; x < len(f); x++ { - rx := toRat(f[x], nil) + rx := toRat(f[x]) if rx == nil { continue } @@ -145,15 +131,22 @@ func SubRat(f ...interface{}) *Rat { // Abs sets r to |r| (the absolute value of r) and return it. // func (r *Rat) Abs() *Rat { + if r == nil { + return nil + } r.Rat.Abs(&r.Rat) return r } // // Add sets r to `r+g` and return the r as the result. +// If g is not convertable to Rat it will equal to r+0. // func (r *Rat) Add(g interface{}) *Rat { - y := toRat(g, nil) + if r == nil { + return nil + } + y := toRat(g) if y == nil { // Its equal to `r+0` return r @@ -167,6 +160,9 @@ func (r *Rat) Add(g interface{}) *Rat { // separator. // func (r *Rat) Humanize(thousandSep, decimalSep string) string { + if r == nil { + return "0" + } var ( raw = r.String() parts = strings.SplitN(raw, ".", 2) @@ -195,23 +191,30 @@ func (r *Rat) Humanize(thousandSep, decimalSep string) string { // It will return math.MinInt64, if the value is lower than MinInt64. // func (r *Rat) Int64() int64 { + if r == nil { + return 0 + } s := strings.Split(r.String(), ".")[0] i64, _ := strconv.ParseInt(s, 10, 64) return i64 } // -// IsEqual will return true if `r == g`. +// IsEqual will return true if `r == g`, including when r and g are both nil. +// +// Unlike the standard Cmp(), if the first call to Cmp is not 0, it will try +// to compare the string values of r and g, truncated by +// DefaultDigitPrecision. // func (r *Rat) IsEqual(g interface{}) bool { - y := toRat(g, nil) + y := toRat(g) if y == nil { return r == nil } if r == nil { return false } - if r.Cmp(&y.Rat) == 0 { + if r.Rat.Cmp(&y.Rat) == 0 { return true } if r.String() == y.String() { @@ -222,24 +225,32 @@ func (r *Rat) IsEqual(g interface{}) bool { // // IsGreater will return true if `r > g`. +// If g is not convertable to Rat it will return false. // func (r *Rat) IsGreater(g interface{}) bool { - y := toRat(g, nil) + if r == nil { + return false + } + y := toRat(g) if y == nil { return false } - return r.Cmp(&y.Rat) > 0 + return r.Rat.Cmp(&y.Rat) > 0 } // // IsGreaterOrEqual will return true if `r >= g`. +// If g is not convertable to Rat it will return false. // func (r *Rat) IsGreaterOrEqual(g interface{}) bool { - y := toRat(g, nil) + if r == nil { + return false + } + y := toRat(g) if y == nil { return false } - return r.Cmp(&y.Rat) >= 0 + return r.Rat.Cmp(&y.Rat) >= 0 } // @@ -249,58 +260,76 @@ func (r *Rat) IsGreaterThanZero() bool { if r == nil { return false } - return r.Cmp(&ratZero.Rat) > 0 + return r.Rat.Cmp(&ratZero.Rat) > 0 } // // IsLess will return true if `r < g`. +// If r is nill or g is not convertable to Rat it will return false. // func (r *Rat) IsLess(g interface{}) bool { - y := toRat(g, nil) + if r == nil { + return false + } + y := toRat(g) if y == nil { return false } - return r.Cmp(&y.Rat) < 0 + return r.Rat.Cmp(&y.Rat) < 0 } // // IsLessOrEqual will return true if `r <= g`. +// It r is nil or g is not convertable to Rat it will return false. // func (r *Rat) IsLessOrEqual(g interface{}) bool { - y := toRat(g, nil) + if r == nil { + return false + } + y := toRat(g) if y == nil { return false } - return r.Cmp(&y.Rat) <= 0 + return r.Rat.Cmp(&y.Rat) <= 0 } // // IsLessThanZero return true if `r < 0`. // func (r *Rat) IsLessThanZero() bool { - return r.Cmp(&ratZero.Rat) < 0 + if r == nil { + return false + } + return r.Rat.Cmp(&ratZero.Rat) < 0 } // // IsZero will return true if `r == 0`. // func (r *Rat) IsZero() bool { - return r.Cmp(&ratZero.Rat) == 0 + if r == nil { + return false + } + return r.Rat.Cmp(&ratZero.Rat) == 0 } // -// MarshalJSON implement the json.Marshaler interface and return the output of -// String method. +// MarshalJSON implement the json.Marshaler interface. +// It will return the same result as String(). // func (r *Rat) MarshalJSON() ([]byte, error) { var s string if r == nil { - s = "0" + if MarshalJSONAsString { + s = `"0"` + } else { + s = `0` + } } else { s = r.String() - } - if MarshalJSONAsString { - s = `"` + s + `"` + if MarshalJSONAsString { + s = `"` + s + `"` + } } return []byte(s), nil } @@ -310,20 +339,23 @@ func (r *Rat) MarshalJSON() ([]byte, error) { // If g is not convertible to Rat it will return nil. // func (r *Rat) Mul(g interface{}) *Rat { - y := toRat(g, nil) + y := toRat(g) if y == nil { - y = ratZero + return nil } r.Rat.Mul(&r.Rat, &y.Rat) - r.SetString(r.String()) + r.Rat.SetString(r.String()) return r } // // Quo sets r to quotient of `r/g` and return the result as r. -// If g is not convertible to Rat or zero it will return nil. +// If r is nil or g is not convertible to Rat or zero it will return nil. // func (r *Rat) Quo(g interface{}) *Rat { + if r == nil { + return nil + } y := toRat(g) if y == nil || y.IsZero() { return nil @@ -337,12 +369,15 @@ func (r *Rat) Quo(g interface{}) *Rat { // RoundNearestFraction round the fraction to the nearest non-zero value. // // The RoundNearestFraction does not require precision parameter, like in -// other rounds function, but it figure it out based on the last non-zero +// other rounds function, but it will figure it out based on the last non-zero // value from fraction. // // See example for more information. // func (r *Rat) RoundNearestFraction() *Rat { + if r == nil { + return nil + } b := []byte(r.String()) nums := bytes.Split(b, []byte{'.'}) x := 0 @@ -362,7 +397,10 @@ func (r *Rat) RoundNearestFraction() *Rat { // For example, using 2 digit precision, 0.555 would become 0.56. // func (r *Rat) RoundToNearestAway(prec int) *Rat { - r.SetString(r.FloatString(prec)) + if r == nil { + return nil + } + r.Rat.SetString(r.FloatString(prec)) return r } @@ -371,6 +409,9 @@ func (r *Rat) RoundToNearestAway(prec int) *Rat { // For example, using 2 digit precision, 0.555 would become 0.55. // func (r *Rat) RoundToZero(prec int) *Rat { + if r == nil { + return nil + } b := []byte(r.FloatString(prec + 1)) nums := bytes.Split(b, []byte{'.'}) b = b[:0] @@ -379,18 +420,19 @@ func (r *Rat) RoundToZero(prec int) *Rat { b = append(b, '.') b = append(b, nums[1][:prec]...) } - r.SetString(string(b)) + r.Rat.SetString(string(b)) return r } // // Scan implement the database's sql.Scan interface. // -func (r *Rat) Scan(src interface{}) error { - got := toRat(src, r) +func (r *Rat) Scan(v interface{}) error { + got := toRat(v) if got == nil { - return fmt.Errorf("Rat.Scan: unknown type %T", src) + return fmt.Errorf("Rat.Scan: unknown type %T", v) } + r.Rat.Set(&got.Rat) return nil } @@ -430,9 +472,13 @@ func (r *Rat) String() string { // // Sub sets r to rounded difference `r-g` and return r. +// If g is not convertable to Rat, it will return as r-0. // func (r *Rat) Sub(g interface{}) *Rat { - y := toRat(g, nil) + if r == nil { + return nil + } + y := toRat(g) if y == nil { // Its equal to `r-0`. return r @@ -449,8 +495,7 @@ func (r *Rat) UnmarshalJSON(in []byte) (err error) { r.SetInt64(0) _, ok := r.Rat.SetString(string(in)) if !ok { - return fmt.Errorf("Rat.UnmarshalJSON:"+ - " cannot convert %T(%v) to Rat", in, in) + return fmt.Errorf("Rat.UnmarshalJSON: cannot convert %T(%v) to Rat", in, in) } return nil } @@ -458,6 +503,7 @@ func (r *Rat) UnmarshalJSON(in []byte) (err error) { // // Value return the []byte value for database/sql, as defined in // sql/driver.Valuer. +// It will return "0" if r is nil. // func (r *Rat) Value() (driver.Value, error) { var s string @@ -470,13 +516,12 @@ func (r *Rat) Value() (driver.Value, error) { } // -// toRat convert any type to Rat or nil if type is unknown. -// If in is not nil, it will be set to out. +// toRat convert v type to Rat or nil if v type is unknown. // -func toRat(v interface{}, in *Rat) (out *Rat) { +func toRat(g interface{}) (out *Rat) { out = &Rat{} - switch v := v.(type) { + switch v := g.(type) { case []byte: if len(v) == 0 { out.SetInt64(0) @@ -488,7 +533,7 @@ func toRat(v interface{}, in *Rat) (out *Rat) { } } case string: - if len(v) == 0 { + if len(v) == 0 || v == "0" { out.SetInt64(0) } else { // Replace the underscore character, so we can write the @@ -508,6 +553,12 @@ func toRat(v interface{}, in *Rat) (out *Rat) { out.SetInt64(int64(v)) case int64: out.SetInt64(v) + case uint: + out.SetUint64(uint64(v)) + case uint16: + out.SetUint64(uint64(v)) + case uint32: + out.SetUint64(uint64(v)) case uint64: out.SetUint64(v) case float32: @@ -515,22 +566,29 @@ func toRat(v interface{}, in *Rat) (out *Rat) { case float64: out.SetFloat64(v) case Rat: - out = &v + out.Rat.Set(&v.Rat) case *Rat: - out = v + if v == nil { + out.SetInt64(0) + } else { + out.Rat.Set(&v.Rat) + } case big.Rat: - out.Rat = v + out.Rat.Set(&v) case *big.Rat: - out.Rat = *v + if v == nil { + out.SetInt64(0) + } else { + out.Rat.Set(v) + } case *big.Int: - out.SetInt(v) + if v == nil { + out.Rat.SetInt64(0) + } else { + out.Rat.SetInt(v) + } default: return nil } - if in != nil { - in.Set(&out.Rat) - } else { - in = out - } return out } diff --git a/lib/math/big/rat_example_test.go b/lib/math/big/rat_example_test.go index dc5a1e8c..e8ae7b26 100644 --- a/lib/math/big/rat_example_test.go +++ b/lib/math/big/rat_example_test.go @@ -5,14 +5,163 @@ package big import ( + "encoding/json" "fmt" + "math" + "math/big" ) +func ExampleAddRat() { + fmt.Println(AddRat()) + fmt.Println(AddRat(nil)) + fmt.Println(AddRat("a")) + fmt.Println(AddRat(0, 0.0001)) + fmt.Println(AddRat("1.007", "a", "2.003")) // Invalid parameter "a" is ignored. + //Output: + //0 + //0 + //0 + //0.0001 + //3.01 +} + +func ExampleMulRat() { + fmt.Println(MulRat()) + fmt.Println(MulRat(nil)) + fmt.Println(MulRat("a")) + fmt.Println(MulRat(0, 1)) + fmt.Println(MulRat(6, "a", "0.3")) // Invalid parameter "a" is ignored. + //Output: + //0 + //0 + //0 + //0 + //1.8 +} + +func ExampleNewRat() { + var ( + stdNilInt *big.Int + stdNilRat *big.Rat + stdRat big.Rat + nilRat *Rat + ) + inputs := []interface{}{ + nil, + []byte{}, + "", + []byte("a"), + "0", + "0.0000_0001", + []byte("14687233442.06916608"), + "14_687_233_442.069_166_08", + nilRat, + NewRat("14687233442.06916608"), + *NewRat("14687233442.06916608"), + stdNilRat, + stdRat, + big.NewRat(14687233442, 100_000_000), + *big.NewRat(14687233442, 100_000_000), + uint16(math.MaxUint16), + uint32(math.MaxUint32), + uint64(math.MaxUint64), + stdNilInt, + big.NewInt(100_000_000), + } + + for _, v := range inputs { + fmt.Println(NewRat(v)) + } + //Output: + //0 + //0 + //0 + //0 + //0 + //0.00000001 + //14687233442.06916608 + //14687233442.06916608 + //0 + //14687233442.06916608 + //14687233442.06916608 + //0 + //0 + //146.87233442 + //146.87233442 + //65535 + //4294967295 + //18446744073709551615 + //0 + //100000000 +} + +func ExampleQuoRat() { + fmt.Println(QuoRat()) + fmt.Println(QuoRat(nil)) + fmt.Println(QuoRat("a")) + fmt.Println(QuoRat("0")) + fmt.Println(QuoRat(2, 0, 2)) + fmt.Println(QuoRat(6, "a", "0.3")) + fmt.Println(QuoRat(0, 1)) + fmt.Println(QuoRat(4651, 272)) + fmt.Println(QuoRat(int64(1815507979407), NewRat(100000000))) + fmt.Println(QuoRat("25494300", "25394000000")) + + //Output: + //0 + //0 + //0 + //0 + //0 + //0 + //0 + //17.0992647 + //18155.07979407 + //0.00100395 +} + +func ExampleSubRat() { + fmt.Println(SubRat()) + fmt.Println(SubRat(nil)) + fmt.Println(SubRat("a")) + fmt.Println(SubRat(0, 1)) + fmt.Println(SubRat(6, "a", "0.3")) + //Output: + //0 + //0 + //0 + //-1 + //5.7 +} + +func ExampleRat_Abs() { + fmt.Println(NewRat(nil).Abs()) + fmt.Println(NewRat("-1").Abs()) + fmt.Println(NewRat("-0.00001").Abs()) + fmt.Println(NewRat("1").Abs()) + //Output: + //0 + //1 + //0.00001 + //1 +} + +func ExampleRat_Add() { + fmt.Println(NewRat(nil).Add(nil)) + fmt.Println(NewRat(1).Add(nil)) + fmt.Println(NewRat(1).Add(1)) + //Output: + //0 + //1 + //2 +} + func ExampleRat_Humanize() { var ( thousandSep = "." decimalSep = "," ) + fmt.Printf("%s\n", NewRat(nil).Humanize(thousandSep, decimalSep)) fmt.Printf("%s\n", NewRat("0").Humanize(thousandSep, decimalSep)) fmt.Printf("%s\n", NewRat("0.1234").Humanize(thousandSep, decimalSep)) fmt.Printf("%s\n", NewRat("100").Humanize(thousandSep, decimalSep)) @@ -23,6 +172,7 @@ func ExampleRat_Humanize() { fmt.Printf("%s\n", NewRat("100000.234").Humanize(thousandSep, decimalSep)) //Output: //0 + //0 //0,1234 //100 //100,1234 @@ -38,14 +188,288 @@ func ExampleRat_Int64() { fmt.Printf("MinInt64: %d\n", NewRat("-9223372036854775808").Int64()) fmt.Printf("MinInt64-1: %d\n", NewRat("-9223372036854775809").Int64()) + fmt.Println(NewRat(nil).Int64()) + fmt.Println(NewRat("0.000_000_001").Int64()) + fmt.Println(NewRat("0.5").Int64()) + fmt.Println(NewRat("0.6").Int64()) + fmt.Println(NewRat("4011144900.02438879").Mul(100000000).Int64()) + fmt.Println(QuoRat("128_900", "0.000_0322").Int64()) + fmt.Println(QuoRat(128900, 3220).Mul(100000000).Int64()) + fmt.Println(QuoRat(25494300, QuoRat(25394000000, 100000000)).Int64()) + //Output: //MaxInt64: 9223372036854775807 //MaxInt64+1: 9223372036854775807 //MinInt64: -9223372036854775808 //MinInt64-1: -9223372036854775808 + //0 + //0 + //0 + //0 + //401114490002438879 + //4003105590 + //4003105590 + //100394 +} + +func ExampleRat_IsEqual() { + f := NewRat(1) + + fmt.Println(NewRat(nil).IsEqual(0)) + fmt.Println(f.IsEqual("a")) + fmt.Println(f.IsEqual("1")) + fmt.Println(f.IsEqual(1.1)) + fmt.Println(f.IsEqual(byte(1))) + fmt.Println(f.IsEqual(int(1))) + fmt.Println(f.IsEqual(int32(1))) + fmt.Println(f.IsEqual(float32(1))) + fmt.Println(f.IsEqual(NewRat(1))) + // Equal due to String() truncation to DefaultDigitPrecision (8) digits. + fmt.Println(NewRat("0.1234567890123").IsEqual("0.12345678")) + + //Output: + //false + //false + //true + //false + //true + //true + //true + //true + //true + //true +} + +func ExampleRat_IsGreater() { + r := NewRat("0.000_000_5") + + fmt.Println(NewRat(nil).IsGreater(0)) + fmt.Println(r.IsGreater(nil)) + fmt.Println(r.IsGreater("0.000_000_5")) + fmt.Println(r.IsGreater("0.000_000_49999")) + + //Output: + //false + //false + //false + //true +} + +func ExampleRat_IsGreaterOrEqual() { + r := NewRat("0.000_000_5") + + fmt.Println(NewRat(nil).IsGreaterOrEqual(0)) + fmt.Println(r.IsGreaterOrEqual(nil)) + fmt.Println(r.IsGreaterOrEqual("0.000_000_500_000_000_001")) + fmt.Println(r.IsGreaterOrEqual("0.000_000_5")) + fmt.Println(r.IsGreaterOrEqual("0.000_000_49999")) + + //Output: + //false + //false + //false + //true + //true +} + +func ExampleRat_IsGreaterThanZero() { + fmt.Println(NewRat(nil).IsGreaterThanZero()) + fmt.Println(NewRat(0).IsGreaterThanZero()) + fmt.Println(NewRat("-0.000_000_000_000_000_001").IsGreaterThanZero()) + fmt.Println(NewRat("0.000_000_000_000_000_001").IsGreaterThanZero()) + + //Output: + //false + //false + //false + //true +} + +func ExampleRat_IsLess() { + r := NewRat("0.000_000_5") + + fmt.Println(NewRat(nil).IsLess(0)) + fmt.Println(r.IsLess(nil)) + fmt.Println(r.IsLess("0.000_000_5")) + fmt.Println(r.IsLess("0.000_000_49")) + fmt.Println(r.IsLess("0.000_000_500_000_000_001")) + + //Output: + //false + //false + //false + //false + //true +} + +func ExampleRat_IsLessOrEqual() { + r := NewRat("0.000_000_5") + + fmt.Println(NewRat(nil).IsLessOrEqual(r)) + fmt.Println(r.IsLessOrEqual(nil)) + fmt.Println(r.IsLessOrEqual("0.000_000_5")) + fmt.Println(r.IsLessOrEqual("0.000_000_49")) + fmt.Println(r.IsLessOrEqual("0.000_000_500_000_000_001")) + + //Output: + //false + //false + //true + //false + //true +} + +func ExampleRat_IsLessThanZero() { + fmt.Println(NewRat(nil).IsLessThanZero()) + fmt.Println(NewRat(byte(0)).IsLessThanZero()) + fmt.Println(NewRat("-0.000_000_000_000_000_001").IsLessThanZero()) + fmt.Println(NewRat("0.000_000_000_000_000_001").IsLessThanZero()) + + //Output: + //false + //false + //true + //false +} + +func ExampleRat_IsZero() { + fmt.Println(NewRat(nil).IsZero()) + fmt.Println(NewRat(byte(0)).IsZero()) + fmt.Println(NewRat(byte(-0)).IsZero()) + fmt.Println(NewRat("-0.000_000_000_000_000_001").IsZero()) + fmt.Println(NewRat("0.000_000_000_000_000_001").IsZero()) + + //Output: + //false + //true + //true + //false + //false +} + +func ExampleRat_MarshalJSON() { + inputs := []string{ + "", + "a", + "0.0000_0000", + "0.1", + "0.0000_0001", + "0.0000_0000_1", // Truncated by DefaultDigitPrecision. + "1234567890.0", + "64.23738872403", // Truncated by DefaultDigitPrecision. + "0.1234567890", + "142660378.65368736", + "9193394308.85771370", + "14687233442.06916608", + } + + MarshalJSONAsString = true + for _, in := range inputs { + out, _ := NewRat(in).MarshalJSON() + fmt.Printf("%s\n", out) + } + + // Setting this to false will make the JSON output become number. + MarshalJSONAsString = false + + for _, in := range inputs { + out, _ := NewRat(in).MarshalJSON() + fmt.Printf("%s\n", out) + } + + //Output: + //"0" + //"0" + //"0" + //"0.1" + //"0.00000001" + //"0" + //"1234567890" + //"64.23738872" + //"0.12345678" + //"142660378.65368736" + //"9193394308.8577137" + //"14687233442.06916608" + //0 + //0 + //0 + //0.1 + //0.00000001 + //0 + //1234567890 + //64.23738872 + //0.12345678 + //142660378.65368736 + //9193394308.8577137 + //14687233442.06916608 +} + +func ExampleRat_MarshalJSON_withStruct() { + type T struct { + V *Rat + } + + inputs := []T{ + {V: nil}, + {V: NewRat(0)}, + {V: NewRat("0.1234567890")}, + } + + MarshalJSONAsString = true + for _, in := range inputs { + out, _ := json.Marshal(&in) + fmt.Printf("%s\n", out) + } + + MarshalJSONAsString = false + for _, in := range inputs { + out, _ := json.Marshal(&in) + fmt.Printf("%s\n", out) + } + + //Output: + //{"V":null} + //{"V":"0"} + //{"V":"0.12345678"} + //{"V":null} + //{"V":0} + //{"V":0.12345678} +} + +func ExampleRat_Mul() { + const ( + defValue = "14687233442.06916608" + ) + + fmt.Println(NewRat(defValue).Mul("a")) + fmt.Println(NewRat(defValue).Mul("0")) + fmt.Println(NewRat(defValue).Mul(defValue)) + fmt.Println(NewRat("1.06916608").Mul("1.06916608")) + //Output: + //0 + //0 + //215714826181834884090.46087866 + //1.1431161 +} + +func ExampleRat_Quo() { + const ( + defValue = "14687233442.06916608" + ) + + fmt.Println(NewRat(nil).Quo(1)) + fmt.Println(NewRat(defValue).Quo(nil)) + fmt.Println(NewRat(defValue).Quo("a")) + fmt.Println(NewRat(defValue).Quo("100_000_000")) + //Output: + //0 + //0 + //0 + //146.87233442 } func ExampleRat_RoundNearestFraction() { + fmt.Printf("nil: %s\n", NewRat(nil).RoundNearestFraction()) fmt.Printf("0.000000001: %s\n", NewRat("0").RoundNearestFraction()) // Affected by DefaultDigitPrecision (8) fmt.Printf("0.00545: %s\n", NewRat("0.00545").RoundNearestFraction()) fmt.Printf("0.00555: %s\n", NewRat("0.00555").RoundNearestFraction()) @@ -58,6 +482,7 @@ func ExampleRat_RoundNearestFraction() { fmt.Printf("-0.555: %s\n", NewRat("-0.555").RoundNearestFraction()) fmt.Printf("-0.545: %s\n", NewRat("-0.545").RoundNearestFraction()) //Output: + //nil: 0 //0.000000001: 0 //0.00545: 0.005 //0.00555: 0.006 @@ -72,21 +497,171 @@ func ExampleRat_RoundNearestFraction() { } func ExampleRat_RoundToNearestAway() { + fmt.Printf("nil: %s\n", NewRat(nil).RoundToNearestAway(2)) fmt.Printf("0.0054: %s\n", NewRat("0.0054").RoundToNearestAway(2)) fmt.Printf("0.0054: %s\n", NewRat("0.0054").RoundToNearestAway(1)) fmt.Printf("0.5455: %s\n", NewRat("0.5455").RoundToNearestAway(2)) fmt.Printf("0.5555: %s\n", NewRat("0.5555").RoundToNearestAway(2)) fmt.Printf("0.5566: %s\n", NewRat("0.5567").RoundToNearestAway(2)) fmt.Printf("0.5566: %s\n", NewRat("0.5566").RoundToNearestAway(0)) + + fmt.Printf("0.02514135: %s\n", NewRat("0.02514135").RoundToNearestAway(6)) + fmt.Printf("0.02514145: %s\n", NewRat("0.02514145").RoundToNearestAway(6)) + fmt.Printf("0.02514155: %s\n", NewRat("0.02514155").RoundToNearestAway(6)) + fmt.Printf("0.02514165: %s\n", NewRat("0.02514165").RoundToNearestAway(6)) + fmt.Printf("0.5: %s\n", NewRat("0.5").RoundToNearestAway(0)) fmt.Printf("-0.5: %s\n", NewRat("-0.5").RoundToNearestAway(0)) //Output: + //nil: 0 //0.0054: 0.01 //0.0054: 0 //0.5455: 0.55 //0.5555: 0.56 //0.5566: 0.56 //0.5566: 1 + //0.02514135: 0.025141 + //0.02514145: 0.025141 + //0.02514155: 0.025142 + //0.02514165: 0.025142 //0.5: 1 //-0.5: -1 } + +func ExampleRat_RoundToZero() { + fmt.Println(NewRat(nil).RoundToZero(2)) + fmt.Println(NewRat("0.5455").RoundToZero(2)) + fmt.Println(NewRat("0.5555").RoundToZero(2)) + fmt.Println(NewRat("0.5566").RoundToZero(2)) + fmt.Println(NewRat("0.5566").RoundToZero(0)) + fmt.Println(NewRat("-0.5").RoundToZero(0)) + //Output: + //0 + //0.54 + //0.55 + //0.55 + //0 + //-0 +} + +func ExampleRat_Scan() { + var ( + r = &Rat{} + err error + ) + + inputs := []interface{}{ + 1234, + nil, + "0.0001", + float64(0.0001), + } + for _, in := range inputs { + err = r.Scan(in) + if err != nil { + fmt.Printf("error: %s\n", err) + } + fmt.Println(r) + } + + //Output: + //1234 + //error: Rat.Scan: unknown type <nil> + //1234 + //0.0001 + //0.0001 +} + +func ExampleRat_String() { + inputs := []interface{}{ + nil, + "12345", + "0.00000000", + float64(0.00000000), + "0.1", + float64(0.1), + "0.0000001", + float64(0.0000001), + "0.000000001", // Truncated due to rounding. + "64.23738872403", + float64(64.23738872403), + } + //Output: + //0 + //12345 + //0 + //0 + //0.1 + //0.1 + //0.0000001 + //0.0000001 + //0 + //64.23738872 + //64.23738872 + + for _, in := range inputs { + fmt.Println(NewRat(in).String()) + } +} + +func ExampleRat_Sub() { + fmt.Println(NewRat(nil).Sub(1)) + fmt.Println(NewRat(1).Sub(nil)) + fmt.Println(NewRat(1).Sub(1)) + //Output: + //0 + //1 + //0 +} + +func ExampleRat_UnmarshalJSON_withStruct() { + type T struct { + V *Rat `json:"V"` + W *Rat `json:"W,omitempty"` + } + + inputs := []string{ + `{"V":"ab"}`, + `{}`, + `{"V":}`, + `{"V":0,"W":0}`, + `{"V":"1"}`, + `{"V":0.123456789}`, + `{"V":"0.1234", "W":0.5678}`, + } + for _, in := range inputs { + t := T{} + err := json.Unmarshal([]byte(in), &t) + if err != nil { + fmt.Println(err) + continue + } + fmt.Printf("%s %s\n", t.V, t.W) + } + //Output: + //Rat.UnmarshalJSON: cannot convert []uint8([97 98]) to Rat + //0 0 + //invalid character '}' looking for beginning of value + //0 0 + //1 0 + //0.12345678 0 + //0.1234 0.5678 +} + +func ExampleRat_Value() { + inputs := []interface{}{ + nil, + 0, + 1.2345, + "12345.6789_1234_5678_9", + } + for _, in := range inputs { + out, _ := NewRat(in).Value() + fmt.Printf("%s\n", out) + } + //Output: + //0 + //0 + //1.2345 + //12345.67891234 +} diff --git a/lib/math/big/rat_test.go b/lib/math/big/rat_test.go index 271d14ab..9eb1009a 100644 --- a/lib/math/big/rat_test.go +++ b/lib/math/big/rat_test.go @@ -5,303 +5,16 @@ package big import ( - "encoding/json" - "fmt" - "math/big" - "strings" "testing" "github.com/shuLhan/share/lib/test" ) -func TestAddRat(t *testing.T) { - cases := []struct { - ins []interface{} - exp *Rat - }{{ - ins: nil, - }, { - ins: []interface{}{"a"}, - }, { - ins: []interface{}{0, 0.0001}, - exp: NewRat(0.0001), - }, { - ins: []interface{}{"1.007", "a", "2.003"}, - exp: NewRat("3.01"), - }} - - for _, c := range cases { - got := AddRat(c.ins...) - test.Assert(t, "AddRat", c.exp, got) - } -} - -func TestMulRat(t *testing.T) { - cases := []struct { - ins []interface{} - exp *Rat - }{{ - ins: nil, - }, { - ins: []interface{}{"a"}, - }, { - ins: []interface{}{0, 1}, - exp: NewRat(0), - }, { - ins: []interface{}{ - NewRat(6), - "a", - NewRat("0.3"), - }, - exp: NewRat("1.8"), - }} - - for _, c := range cases { - got := MulRat(c.ins...) - test.Assert(t, "MulRat", c.exp, got) - } -} - -func TestQuoRat(t *testing.T) { - cases := []struct { - ins []interface{} - exp string - }{{ - ins: nil, - }, { - ins: []interface{}{"a"}, - }, { - ins: []interface{}{0, 1}, - exp: "0", - }, { - ins: []interface{}{ - NewRat(6), - "a", - NewRat("0.3"), - }, - exp: "", - }, { - ins: []interface{}{ - 4651, - 272, - }, - exp: "17.0992647", - }, { - ins: []interface{}{ - int64(1815507979407), - NewRat(100000000), - }, - exp: "18155.07979407", - }, { - ins: []interface{}{ - "25494300", - "25394000000", - }, - exp: "0.00100395", - }, { - ins: []interface{}{2, 0, 2}, - exp: "", - }} - - for _, c := range cases { - got := QuoRat(c.ins...) - if got == nil { - test.Assert(t, "QuoRat", c.exp, "") - continue - } - test.Assert(t, "QuoRat", c.exp, got.String()) - } -} - -func TestSubRat(t *testing.T) { - cases := []struct { - ins []interface{} - exp *Rat - }{{ - ins: nil, - }, { - ins: []interface{}{"a"}, - }, { - ins: []interface{}{0, 1}, - exp: NewRat(-1), - }, { - ins: []interface{}{ - NewRat(6), - "a", - NewRat("0.3"), - }, - exp: NewRat("5.7"), - }} - - for _, c := range cases { - got := SubRat(c.ins...) - test.Assert(t, "SubRat", c.exp, got) - } -} - -func TestNewRat(t *testing.T) { - cases := []struct { - v interface{} - exp *Rat - }{{ - v: []byte{}, - exp: NewRat(0), - }, { - v: "", - exp: NewRat(0), - }, { - v: []byte("a"), - }, { - v: []byte("14687233442.06916608"), - exp: NewRat("14687233442.06916608"), - }, { - v: "14687233442.06916608", - exp: NewRat("14687233442.06916608"), - }, { - v: NewRat("14687233442.06916608"), - exp: NewRat("14687233442.06916608"), - }, { - v: *(NewRat("14687233442.06916608")), - exp: NewRat("14687233442.06916608"), - }, { - v: big.NewRat(14687233442, 100_000_000), - exp: NewRat("146.87233442"), - }, { - v: *(big.NewRat(14687233442, 100_000_000)), - exp: NewRat("146.87233442"), - }, { - v: uint64(18446744073709551615), - exp: NewRat("18446744073709551615"), - }, { - v: big.NewInt(100_000_000), - exp: NewRat("100_000_000"), - }} - - for _, c := range cases { - got := NewRat(c.v) - test.Assert(t, fmt.Sprintf("NewRat: %T(%v)", c.v, c.v), - c.exp, got) - } -} - -func TestRat_Abs(t *testing.T) { - cases := []struct { - r *Rat - exp string - }{{ - r: NewRat("-1"), - exp: "1", - }, { - r: NewRat("0.0000"), - exp: "0", - }, { - r: NewRat("1"), - exp: "1", - }} - - for _, c := range cases { - test.Assert(t, "Abs()", c.exp, c.r.Abs().String()) - } -} - -func TestRat_Add(t *testing.T) { - cases := []struct { - got *Rat - in interface{} - exp *Rat - }{{ - got: NewRat(1), - in: nil, - exp: NewRat(1), - }, { - got: NewRat(1), - in: 1, - exp: NewRat(2), - }} - - for _, c := range cases { - t.Logf("Add %T(%v)", c.in, c.in) - - c.got.Add(c.in) - - test.Assert(t, "Add", c.exp, c.got) - } -} - -func TestRat_Int64(t *testing.T) { - cases := []struct { - r *Rat - exp int64 - }{{ - r: NewRat("0.000000001"), - exp: 0, - }, { - r: NewRat("0.5"), - exp: 0, - }, { - r: NewRat("0.6"), - exp: 0, - }, { - r: NewRat("4011144900.02438879").Mul(100000000), - exp: 401114490002438879, - }, { - r: QuoRat("128_900", "0.000_0322"), - exp: 4003105590, - }, { - r: QuoRat(128900, 3220).Mul(100000000), - exp: 4003105590, - }, { - r: QuoRat(25494300, QuoRat(25394000000, 100000000)), - exp: 100394, - }} - - for _, c := range cases { - got := c.r.Int64() - test.Assert(t, fmt.Sprintf("Int64 of %s", c.r), c.exp, got) - } -} - -func TestRat_IsEqual(t *testing.T) { - f := NewRat(1) - - cases := []struct { - g interface{} - exp bool - }{{ - g: "a", - }, { - g: 1.1, - }, { - g: byte(1), - exp: true, - }, { - g: int(1), - exp: true, - }, { - g: int32(1), - exp: true, - }, { - g: int64(1), - exp: true, - }, { - g: float32(1), - exp: true, - }, { - g: NewRat(1), - exp: true, - }} - - for _, c := range cases { - got := f.IsEqual(c.g) - test.Assert(t, "IsEqual", c.exp, got) +func TestRat_IsEqual_unexported(t *testing.T) { + type A struct { + r *Rat } -} - -type A struct { - r *Rat -} -func TestRat_IsEqual_unexported(t *testing.T) { exp := &A{ r: NewRat(10), } @@ -314,507 +27,7 @@ func TestRat_IsEqual_unexported(t *testing.T) { }, }} - for x, c := range cases { - test.Assert(t, fmt.Sprintf("unexported field %d", x), exp, c.got) - } -} - -func TestRat_IsGreater(t *testing.T) { - r := NewRat("0.000_000_5") - - cases := []struct { - in interface{} - exp bool - }{{ - in: nil, - }, { - in: "0.000_000_5", - }, { - in: "0.000_000_49", - exp: true, - }} for _, c := range cases { - got := r.IsGreater(c.in) - test.Assert(t, fmt.Sprintf("IsGreater %s", c.in), - c.exp, got) - } -} - -func TestRat_IsGreaterOrEqual(t *testing.T) { - r := NewRat("0.000_000_5") - - cases := []struct { - in interface{} - exp bool - }{{ - in: nil, - }, { - in: "0.000_000_500_000_000_001", - }, { - in: "0.000_000_5", - exp: true, - }, { - in: "0.000_000_49", - exp: true, - }} - for _, c := range cases { - got := r.IsGreaterOrEqual(c.in) - test.Assert(t, fmt.Sprintf("IsGreaterOrEqual %s", c.in), - c.exp, got) - } -} - -func TestRat_IsGreaterThanZero(t *testing.T) { - cases := []struct { - in interface{} - exp bool - }{{ - in: byte(0), - }, { - in: "-0.000_000_000_000_000_001", - }, { - in: "0.000_000_000_000_000_001", - exp: true, - }, { - in: "0.000_000_5", - exp: true, - }} - for _, c := range cases { - got := NewRat(c.in).IsGreaterThanZero() - test.Assert(t, fmt.Sprintf("IsGreaterThanZero %s", c.in), - c.exp, got) - } -} - -func TestRat_IsLess(t *testing.T) { - r := NewRat("0.000_000_5") - - cases := []struct { - in interface{} - exp bool - }{{ - in: nil, - }, { - in: "0.000_000_5", - }, { - in: "0.000_000_49", - }, { - in: "0.000_000_500_000_000_001", - exp: true, - }} - for _, c := range cases { - got := r.IsLess(c.in) - test.Assert(t, fmt.Sprintf("IsLess %s", c.in), - c.exp, got) - } -} - -func TestRat_IsLessOrEqual(t *testing.T) { - r := NewRat("0.000_000_5") - - cases := []struct { - in interface{} - exp bool - }{{ - in: nil, - }, { - in: "0.000_000_5", - exp: true, - }, { - in: "0.000_000_49", - }, { - in: "0.000_000_500_000_000_001", - exp: true, - }} - for _, c := range cases { - got := r.IsLessOrEqual(c.in) - test.Assert(t, fmt.Sprintf("IsLessOrEqual %s", c.in), - c.exp, got) - } -} - -func TestRat_IsLessThanZero(t *testing.T) { - cases := []struct { - in interface{} - exp bool - }{{ - in: byte(0), - }, { - in: "-0.000_000_000_000_000_001", - exp: true, - }, { - in: "0.000_000_000_000_000_001", - }, { - in: "0.000_000_5", - }} - for _, c := range cases { - got := NewRat(c.in).IsLessThanZero() - test.Assert(t, fmt.Sprintf("IsLessThanZero %s", c.in), - c.exp, got) - } -} - -func TestRat_IsZero(t *testing.T) { - cases := []struct { - in interface{} - exp bool - }{{ - in: byte(0), - exp: true, - }, { - in: byte(-0), - exp: true, - }, { - in: "-0.000_000_000_000_000_001", - }, { - in: "0.000_000_000_000_000_001", - }} - for _, c := range cases { - got := NewRat(c.in).IsZero() - test.Assert(t, fmt.Sprintf("IsZero %s", c.in), - c.exp, got) - } -} - -func TestRat_MarshalJSON(t *testing.T) { - cases := []struct { - in string - exp string - }{{ - exp: `"0"`, - }, { - in: "0.00000000", - exp: `"0"`, - }, { - in: "0.1", - exp: `"0.1"`, - }, { - in: "0.0000001", - exp: `"0.0000001"`, - }, { - in: "0.00000001", - exp: `"0.00000001"`, - }, { - in: "0.000000001", - exp: `"0"`, - }, { - in: "1234567890.0", - exp: `"1234567890"`, - }, { - in: "64.23738872403", - exp: `"64.23738872"`, - }, { - in: "0.1234567890", - exp: `"0.12345678"`, - }, { - in: "142660378.65368736", - exp: `"142660378.65368736"`, - }, { - in: "9193394308.85771370", - exp: `"9193394308.8577137"`, - }, { - in: "14687233442.06916608", - exp: `"14687233442.06916608"`, - }} - for _, c := range cases { - var ( - got []byte - err error - ) - r := NewRat(c.in) - got, err = r.MarshalJSON() - if err != nil { - t.Fatal(err) - } - test.Assert(t, fmt.Sprintf("MarshalJSON(%s)", c.in), - c.exp, string(got)) - } -} - -func TestRat_Mul(t *testing.T) { - const ( - defValue = "14687233442.06916608" - ) - - cases := []struct { - got *Rat - in interface{} - exp *Rat - }{{ - got: NewRat(defValue), - in: "a", - exp: NewRat(0), - }, { - got: NewRat(defValue), - in: "0", - exp: NewRat(0), - }, { - got: NewRat(defValue), - in: defValue, - exp: NewRat("215714826181834884090.46087866"), - }, { - got: NewRat("1.06916608"), - in: "1.06916608", - exp: NewRat("1.1431161"), - }} - - for _, c := range cases { - t.Logf("Mul %T(%v)", c.in, c.in) - - c.got.Mul(c.in) - - test.Assert(t, "Mul", c.exp, c.got) - } -} - -func TestRat_Quo(t *testing.T) { - const ( - defValue = "14687233442.06916608" - ) - - cases := []struct { - g interface{} - exp *Rat - }{{ - g: "a", - }, { - g: defValue, - exp: NewRat(1), - }, { - g: "100000000", - exp: NewRat("146.87233442"), - }} - - for _, c := range cases { - r := NewRat(defValue) - got := r.Quo(c.g) - - test.Assert(t, "Quo", c.exp, got) - } -} - -func TestRat_RoundToZero(t *testing.T) { - cases := []struct { - r *Rat - prec int - exp string - }{{ - r: NewRat("0.5455"), - prec: 2, - exp: "0.54", - }, { - r: NewRat("0.5555"), - prec: 2, - exp: "0.55", - }, { - r: NewRat("0.5566"), - prec: 2, - exp: "0.55", - }, { - r: NewRat("0.5566"), - prec: 0, - exp: "0", - }, { - r: NewRat("0.5"), - prec: 0, - exp: "0", - }, { - r: NewRat("-0.5"), - prec: 0, - exp: "-0", - }} - - for _, c := range cases { - got := c.r.RoundToZero(c.prec).String() - test.Assert(t, "RoundToZero", c.exp, got) - } -} - -func TestRat_Scan(t *testing.T) { - cases := []struct { - in interface{} - exp *Rat - expError string - }{{ - in: nil, - expError: "Rat.Scan: unknown type <nil>", - }, { - in: "0.0001", - exp: NewRat("0.0001"), - }, { - in: float64(0.0001), - exp: NewRat(0.0001), - }, { - in: (1.0 / 10000.0), - exp: NewRat(1.0 / 10000.0), - }} - - for _, c := range cases { - r := NewRat(0) - err := r.Scan(c.in) - if err != nil { - test.Assert(t, "Scan error", c.expError, err.Error()) - continue - } - test.Assert(t, fmt.Sprintf("Scan(%T(%v))", c.in, c.in), - c.exp, r) - } -} - -func TestRat_String_fromString(t *testing.T) { - cases := []struct { - in string - exp string - }{{ - in: "12345", - exp: "12345", - }, { - in: "0.00000000", - exp: "0", - }, { - in: "0.1", - exp: "0.1", - }, { - in: "0.0000001", - exp: "0.0000001", - }, { - in: "0.00000001", - exp: "0.00000001", - }, { - in: "0.000000001", - exp: "0", - }, { - in: "1234567890.0", - exp: "1234567890", - }, { - in: "64.23738872403", - exp: "64.23738872", - }, { - in: "0.1234567890", - exp: "0.12345678", - }, { - in: "142660378.65368736", - exp: "142660378.65368736", - }, { - in: "9193394308.85771370", - exp: "9193394308.8577137", - }, { - in: "14687233442.06916608", - exp: "14687233442.06916608", - }} - - for _, c := range cases { - got := MustRat(c.in) - test.Assert(t, c.in, c.exp, got.String()) - } -} - -func TestRat_String_fromFloat64(t *testing.T) { - cases := []struct { - in float64 - exp string - }{{ - in: 0.00000000, - exp: "0", - }, { - in: 0.1, - exp: "0.1", - }, { - in: 0.000_000_1, - exp: "0.0000001", - }, { - in: 0.000_000_01, - exp: "0.00000001", - }, { - in: 0.000000001, - exp: "0", - }, { - in: 1234567890.0, - exp: "1234567890", - }, { - in: 64.23738872403, - exp: "64.23738872", - }, { - in: 0.1234567890, - exp: "0.12345678", - }, { - in: 142660378.65368736, - exp: "142660378.65368735", - }, { - in: 9193394308.85771370, - exp: "9193394308.85771369", - }} - - for _, c := range cases { - got := NewRat(c.in) - test.Assert(t, c.exp, c.exp, got.String()) - } -} - -func TestRat_Sub(t *testing.T) { - cases := []struct { - got *Rat - in interface{} - exp *Rat - }{{ - got: NewRat(1), - in: nil, - exp: NewRat(1), - }, { - got: NewRat(1), - in: 1, - exp: NewRat(0), - }} - - for _, c := range cases { - t.Logf("Sub %T(%v)", c.in, c.in) - - c.got.Sub(c.in) - - test.Assert(t, "Sub", c.exp, c.got) - } -} - -func TestRat_UnmarshalJSON(t *testing.T) { - type T struct { - V *Rat `json:"V"` - } - - cases := []struct { - in []byte - exp *Rat - expError string - }{{ - in: []byte(`{"V":"ab"}`), - expError: "cannot convert []uint8([97 98]) to Rat", - }, { - in: []byte(`{}`), - }, { - in: []byte(`{"V":}`), - expError: `invalid character '}' looking for beginning of value`, - }, { - in: []byte(`{"V":0}`), - exp: NewRat(0), - }, { - in: []byte(`{"V":"1"}`), - exp: NewRat(1), - }, { - in: []byte(`{"V":0.00000001}`), - exp: MustRat("0.00000001"), - }} - - for _, c := range cases { - t.Logf("%q", c.in) - - got := &T{} - err := json.Unmarshal(c.in, &got) - if err != nil { - if strings.Contains(err.Error(), c.expError) { - continue - } - t.Fatalf("expecting error like %q, got %q", c.expError, err.Error()) - } - - test.Assert(t, "", c.exp, got.V) + test.Assert(t, "IsEqual unexported field", exp, c.got) } } |
