chore: set math to main (#23697)
This commit is contained in:
parent
8abca32b6c
commit
edcb427a6e
@ -36,10 +36,79 @@ Ref: https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.j
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [math/v1.5.0](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.5.0) - 2025-01-06
|
||||
|
||||
* [#11783](https://github.com/cosmos/cosmos-sdk/issues/11783) Upstream GDA based decimal type
|
||||
|
||||
## [math/v1.4.0](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.4.0) - 2024-11-20
|
||||
|
||||
### Features
|
||||
|
||||
* [#20034](https://github.com/cosmos/cosmos-sdk/pull/20034) Significantly speedup LegacyDec.QuoTruncate and LegacyDec.QuoRoundUp.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fix [ASA-2024-010: Math](https://github.com/cosmos/cosmos-sdk/security/advisories/GHSA-7225-m954-23v7) Bit length differences between Int and Dec
|
||||
|
||||
|
||||
## [math/v1.3.0](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.3.0) - 2024-02-22
|
||||
|
||||
### Features
|
||||
|
||||
* [#18552](https://github.com/cosmos/cosmos-sdk/pull/18552) Add safe arithmetic operations for `math.Int` that return an error in case of an overflow or any mishap.
|
||||
* [#18421](https://github.com/cosmos/cosmos-sdk/pull/18421) Add mutative api for `LegacyDec.BigInt()`.
|
||||
* [#18874](https://github.com/cosmos/cosmos-sdk/pull/18874) Speedup `math.Int.Mul` by removing a duplicate overflow check
|
||||
* [#19386](https://github.com/cosmos/cosmos-sdk/pull/19386) Speedup `math.Int` overflow checks
|
||||
* [#19466](https://github.com/cosmos/cosmos-sdk/pull/19466) Speedup `math.NewLegacyDec` by one heap allocation
|
||||
* [#19467](https://github.com/cosmos/cosmos-sdk/pull/19467) Slightly speedup `math.LegacyDec` `Ceil` and `MarshalTo` methods
|
||||
* [#19479](https://github.com/cosmos/cosmos-sdk/pull/19479) Speedup `math.LegacyDec` functions that involve `math.Int` by removing a heap allocation. (`Ceil`, `TruncateInt`, `NewLegacyDecFromInt`)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#16266](https://github.com/cosmos/cosmos-sdk/pull/16266) fix: legacy dec power mut zero exponent precision.
|
||||
* [#18519](https://github.com/cosmos/cosmos-sdk/pull/18519) Prevent Overflow in `Dec.Ceil()`.
|
||||
|
||||
## [math/v1.2.0](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.2.0) - 2023-11-07
|
||||
|
||||
### Features
|
||||
|
||||
* [#18247](https://github.com/cosmos/cosmos-sdk/pull/18247) Add mutative api for `Uint.BigInt()`.
|
||||
* [#17803](https://github.com/cosmos/cosmos-sdk/pull/17803) Add mutative api for `Int.BigInt()`.
|
||||
* [#18030](https://github.com/cosmos/cosmos-sdk/pull/18030) Add mutative api for `NewIntFromBigInt`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#18228](https://github.com/cosmos/cosmos-sdk/pull/18228) Fix panic when calling `BigInt()` on an uninitialized `Uint`.
|
||||
* [#18214](https://github.com/cosmos/cosmos-sdk/pull/18214) Ensure that modifying the argument to `NewUIntFromBigInt` doesn't mutate the returned value.
|
||||
* [#18211](https://github.com/cosmos/cosmos-sdk/pull/18211) RelativePow now returns 1 when 0^0, before it was returning the scale factor.
|
||||
* [#17725](https://github.com/cosmos/cosmos-sdk/pull/17725) Fix state break in ApproxRoot. This has been present since math/v1.0.1. It changed the rounding behavior at precision end in an intermediary division from banker's to truncation. The truncation occurs from binary right shift in the case of square roots. The change is now reverted back to banker's rounding universally for any root.
|
||||
|
||||
## [math/v1.1.2](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.1.2) - 2023-08-21
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#17489](https://github.com/cosmos/cosmos-sdk/pull/17489) Revert [#16263](https://github.com/cosmos/cosmos-sdk/pull/16263).
|
||||
|
||||
## [math/v1.1.1](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.1.1) - 2023-08-21
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#17480](https://github.com/cosmos/cosmos-sdk/pull/17480) Fix panic when calling `.Size()` on a nil `math.Int` value.
|
||||
|
||||
## [math/v1.1.0](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.1.0) - 2023-08-19
|
||||
|
||||
### Features
|
||||
|
||||
* [#17427](https://github.com/cosmos/cosmos-sdk/pull/17427) Implement LegacyDec.MulRoundUp that rounds up at precision end.
|
||||
|
||||
### Improvements
|
||||
|
||||
* [#17109](https://github.com/cosmos/cosmos-sdk/pull/17109) Add `.ToLegacyDec()` method on `math.Int` type for converting to `math.LegacyDec`.
|
||||
* [#16263](https://github.com/cosmos/cosmos-sdk/pull/16263) Improved `math/Int.Size` by computing the decimal digits count instead of firstly invoking .Marshal() then checking the length
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#17352](https://github.com/cosmos/cosmos-sdk/pull/17352) Ensure that modifying the argument to `NewIntFromBigInt` doesn't mutate the returned value.
|
||||
* [#16266](https://github.com/cosmos/cosmos-sdk/pull/16266) Fix legacy dec power mut zero exponent precision.
|
||||
|
||||
## [math/v1.0.1](https://github.com/cosmos/cosmos-sdk/releases/tag/math/v1.0.1) - 2023-05-15
|
||||
|
||||
|
||||
1330
math/dec.go
1330
math/dec.go
File diff suppressed because it is too large
Load Diff
353
math/dec_bench_test.go
Normal file
353
math/dec_bench_test.go
Normal file
@ -0,0 +1,353 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecQuotient(b *testing.B) {
|
||||
specs := map[string]struct {
|
||||
dividend, divisor string
|
||||
}{
|
||||
"small/ small": {
|
||||
dividend: "100", divisor: "5",
|
||||
},
|
||||
"big18/ small": {
|
||||
dividend: "999999999999999999", divisor: "10",
|
||||
},
|
||||
"self18/ self18": {
|
||||
dividend: "999999999999999999", divisor: "999999999999999999",
|
||||
},
|
||||
"big18/ big18": {
|
||||
dividend: "888888888888888888", divisor: "444444444444444444",
|
||||
},
|
||||
"decimal18b/ decimal18c": {
|
||||
dividend: "8.88888888888888888", divisor: "4.1234567890123",
|
||||
},
|
||||
"small/ big18": {
|
||||
dividend: "100", divisor: "999999999999999999",
|
||||
},
|
||||
"big34/ big34": {
|
||||
dividend: "9999999999999999999999999999999999", divisor: "1999999999999999999999999999999999",
|
||||
},
|
||||
"negative big34": {
|
||||
dividend: "-9999999999999999999999999999999999", divisor: "999999999999999999999999999",
|
||||
},
|
||||
"decimal small": {
|
||||
dividend: "0.0000000001", divisor: "10",
|
||||
},
|
||||
"decimal small/decimal small ": {
|
||||
dividend: "0.0000000001", divisor: "0.0001",
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
dv, ds := LegacyMustNewDecFromStr(spec.dividend), LegacyMustNewDecFromStr(spec.divisor)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = dv.Quo(ds)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
dv, ds := must(NewDecFromString(spec.dividend)), must(NewDecFromString(spec.divisor))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = dv.Quo(ds)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecSum(b *testing.B) {
|
||||
specs := map[string]struct {
|
||||
summands []string
|
||||
}{
|
||||
"1+2": {
|
||||
summands: []string{"1", "2"},
|
||||
},
|
||||
"small numbers": {
|
||||
summands: []string{"123", "0.2", "3.1415", "15"},
|
||||
},
|
||||
"medium numbers": {
|
||||
summands: []string{"1234.567899", "9991345552.2340134"},
|
||||
},
|
||||
"big18": {
|
||||
summands: []string{"123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678"},
|
||||
},
|
||||
|
||||
"growing numbers": {
|
||||
summands: []string{"1", "100", "1000", "100000", "10000000", "10000000000", "10000000000000", "100000000000000000"},
|
||||
},
|
||||
"decimals": {
|
||||
summands: []string{"0.1", "0.01", "0.001", "0.000001", "0.00000001", "0.00000000001", "0.00000000000001", "0.000000000000000001"},
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
summands := make([]LegacyDec, len(spec.summands))
|
||||
for i, s := range spec.summands {
|
||||
summands[i] = LegacyMustNewDecFromStr(s)
|
||||
}
|
||||
sum := LegacyNewDec(0)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range summands {
|
||||
sum = sum.Add(s)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
summands := make([]Dec, len(spec.summands))
|
||||
for i, s := range spec.summands {
|
||||
summands[i] = must(NewDecFromString(s))
|
||||
}
|
||||
sum := NewDecFromInt64(0)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range summands {
|
||||
sum, _ = sum.Add(s)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecSub(b *testing.B) {
|
||||
specs := map[string]struct {
|
||||
minuend string
|
||||
subtrahends []string
|
||||
}{
|
||||
"100 - 1 - 2": {
|
||||
minuend: "100",
|
||||
subtrahends: []string{"1", "2"},
|
||||
},
|
||||
"small numbers": {
|
||||
minuend: "152.4013",
|
||||
subtrahends: []string{"123", "0.2", "3.1415", "15"},
|
||||
},
|
||||
"10000000 - big18 numbers": {
|
||||
minuend: "10000000",
|
||||
subtrahends: []string{"123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678", "123456789012345678"},
|
||||
},
|
||||
"10000000 - growing numbers": {
|
||||
minuend: "10000000",
|
||||
subtrahends: []string{"1", "100", "1000", "100000", "10000000", "10000000000", "10000000000000", "100000000000000000"},
|
||||
},
|
||||
"10000000 shrinking decimals": {
|
||||
minuend: "10000000",
|
||||
subtrahends: []string{"0.1", "0.01", "0.001", "0.000001", "0.00000001", "0.00000000001", "0.00000000000001", "0.000000000000000001"},
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
summands := make([]LegacyDec, len(spec.subtrahends))
|
||||
for i, s := range spec.subtrahends {
|
||||
summands[i] = LegacyMustNewDecFromStr(s)
|
||||
}
|
||||
diff := LegacyMustNewDecFromStr(spec.minuend)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range summands {
|
||||
diff = diff.Sub(s)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
summands := make([]Dec, len(spec.subtrahends))
|
||||
for i, s := range spec.subtrahends {
|
||||
summands[i] = must(NewDecFromString(s))
|
||||
}
|
||||
diff := must(NewDecFromString(spec.minuend))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range summands {
|
||||
diff, _ = diff.Sub(s)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecMul(b *testing.B) {
|
||||
specs := map[string]struct {
|
||||
multiplier, multiplicant string
|
||||
}{
|
||||
"small/ small": {
|
||||
multiplier: "100", multiplicant: "5",
|
||||
},
|
||||
"big18/ small": {
|
||||
multiplier: "999999999999999999", multiplicant: "10",
|
||||
},
|
||||
"self18/ self18": {
|
||||
multiplier: "999999999999999999", multiplicant: "999999999999999999",
|
||||
},
|
||||
"big18/ big18": {
|
||||
multiplier: "888888888888888888", multiplicant: "444444444444444444",
|
||||
},
|
||||
"decimal18b/ decimal18c": {
|
||||
multiplier: "8.88888888888888888", multiplicant: "4.1234567890123",
|
||||
},
|
||||
"small/ big18": {
|
||||
multiplier: "100", multiplicant: "999999999999999999",
|
||||
},
|
||||
"big34/ big34": {
|
||||
multiplier: "9999999999999999999999999999999999", multiplicant: "1999999999999999999999999999999999",
|
||||
},
|
||||
"negative big34": {
|
||||
multiplier: "-9999999999999999999999999999999999", multiplicant: "999999999999999999999999999",
|
||||
},
|
||||
"decimal small": {
|
||||
multiplier: "0.0000000001", multiplicant: "10",
|
||||
},
|
||||
"decimal small/decimal small ": {
|
||||
multiplier: "0.0000000001", multiplicant: "0.0001",
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
dv, ds := LegacyMustNewDecFromStr(spec.multiplier), LegacyMustNewDecFromStr(spec.multiplicant)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = dv.Mul(ds)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
dv, ds := must(NewDecFromString(spec.multiplier)), must(NewDecFromString(spec.multiplicant))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = dv.Mul(ds)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecMarshalUnmarshal(b *testing.B) {
|
||||
specs := map[string]struct {
|
||||
src string
|
||||
}{
|
||||
"small": {
|
||||
src: "1",
|
||||
},
|
||||
"big18": {
|
||||
src: "999999999999999999",
|
||||
},
|
||||
"negative big34": {
|
||||
src: "9999999999999999999999999999999999",
|
||||
},
|
||||
"decimal": {
|
||||
src: "12345.678901234341",
|
||||
},
|
||||
}
|
||||
for name, spec := range specs {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
src := LegacyMustNewDecFromStr(spec.src)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bz, err := src.Marshal()
|
||||
require.NoError(b, err)
|
||||
var d LegacyDec
|
||||
require.NoError(b, d.Unmarshal(bz))
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
src := must(NewDecFromString(spec.src))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bz, err := src.Marshal()
|
||||
require.NoError(b, err)
|
||||
var d Dec
|
||||
require.NoError(b, d.Unmarshal(bz))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyDecAndNewDecQuoInteger(b *testing.B) {
|
||||
legacyB1 := LegacyNewDec(100)
|
||||
newB1 := NewDecFromInt64(100)
|
||||
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = legacyB1.Quo(LegacyNewDec(1))
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = newB1.QuoInteger(NewDecFromInt64(1))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacyAddAndDecAdd(b *testing.B) {
|
||||
legacyB1 := LegacyNewDec(100)
|
||||
legacyB2 := LegacyNewDec(5)
|
||||
newB1 := NewDecFromInt64(100)
|
||||
newB2 := NewDecFromInt64(5)
|
||||
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = legacyB1.Add(legacyB2)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = newB1.Add(newB2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacySubAndDecMul(b *testing.B) {
|
||||
legacyB1 := LegacyNewDec(100)
|
||||
legacyB2 := LegacyNewDec(5)
|
||||
newB1 := NewDecFromInt64(100)
|
||||
newB2 := NewDecFromInt64(5)
|
||||
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = legacyB1.Mul(legacyB2)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = newB1.Mul(newB2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCompareLegacySubAndDecSub(b *testing.B) {
|
||||
legacyB1 := LegacyNewDec(100)
|
||||
legacyB2 := LegacyNewDec(5)
|
||||
newB1 := NewDecFromInt64(100)
|
||||
newB2 := NewDecFromInt64(5)
|
||||
|
||||
b.Run("LegacyDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = legacyB1.Sub(legacyB2)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("NewDec", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = newB1.Sub(newB2)
|
||||
}
|
||||
})
|
||||
}
|
||||
323
math/dec_examples_test.go
Normal file
323
math/dec_examples_test.go
Normal file
@ -0,0 +1,323 @@
|
||||
package math
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleDec() {
|
||||
d := NewDecFromInt64(1) // 1
|
||||
fmt.Println(d.String())
|
||||
|
||||
d = NewDecWithExp(-1234, -3) // -1.234
|
||||
fmt.Println(d.String())
|
||||
d = NewDecWithExp(1234, 0) // 1234
|
||||
fmt.Println(d.String())
|
||||
d = NewDecWithExp(1234, 1) // 12340
|
||||
fmt.Println(d.String())
|
||||
|
||||
// scientific notation
|
||||
d, err := NewDecFromString("1.23E+4") // 12300
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(d.String())
|
||||
|
||||
// decimal notation
|
||||
d, err = NewDecFromString("1.234")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(d.String())
|
||||
|
||||
// Output: 1
|
||||
// -1.234
|
||||
// 1234
|
||||
// 12340
|
||||
// 12300
|
||||
// 1.234
|
||||
}
|
||||
|
||||
func ExampleDec_Add() {
|
||||
sum, err := NewDecFromInt64(1).Add(NewDecFromInt64(1)) // 1 + 1 = 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const maxExp = 100_000
|
||||
_, err = NewDecWithExp(1, maxExp).Add(NewDecFromInt64(1)) // 1E+1000000 + 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = NewDecWithExp(1, maxExp).Add(NewDecWithExp(1, maxExp)) // 1E+1000000 + 1E+1000000
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.Text('E'))
|
||||
|
||||
// the max exponent must not be exceeded
|
||||
_, err = NewDecWithExp(1, maxExp+1).Add(NewDecFromInt64(1)) // 1E+1000001 + 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
const minExp = -100_000
|
||||
// same for min exponent
|
||||
_, err = NewDecWithExp(1, minExp-1).Add(NewDecFromInt64(1)) // 1E-1000001 + 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
// not even by adding 0
|
||||
_, err = NewDecWithExp(1, minExp-1).Add(NewDecFromInt64(0)) // 1E-1000001 + 0
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
// Output: 2
|
||||
// 2E+100000
|
||||
// add: exponent out of range: invalid decimal
|
||||
// add: exponent out of range: invalid decimal
|
||||
// add: exponent out of range: invalid decimal
|
||||
}
|
||||
|
||||
func ExampleDec_Sub() {
|
||||
sum, err := NewDecFromInt64(2).Sub(NewDecFromInt64(1)) // 2 - 1
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const maxExp = 100_000
|
||||
_, err = NewDecWithExp(1, maxExp).Sub(NewDecFromInt64(1)) // 1E+1000000 - 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = NewDecWithExp(1, maxExp).Sub(NewDecWithExp(1, maxExp)) // 1E+1000000 - 1E+1000000
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.Text('E'))
|
||||
|
||||
// the max exponent must not be exceeded
|
||||
_, err = NewDecWithExp(1, maxExp+1).Sub(NewDecFromInt64(1)) // 1E+1000001 - 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
const minExp = -100_000
|
||||
// same for min exponent
|
||||
_, err = NewDecWithExp(1, minExp-1).Sub(NewDecFromInt64(1)) // 1E-1000001 - 1
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
// not even by adding 0
|
||||
_, err = NewDecWithExp(1, minExp-1).Sub(NewDecFromInt64(0)) // 1E-1000001 - 0
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
// Output: 1
|
||||
// 0E+100000
|
||||
// sub: exponent out of range: invalid decimal
|
||||
// sub: exponent out of range: invalid decimal
|
||||
// sub: exponent out of range: invalid decimal
|
||||
}
|
||||
|
||||
func ExampleDec_Quo() {
|
||||
sum, err := NewDecFromInt64(6).Quo(NewDecFromInt64(2)) // 6 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(7).Quo(NewDecFromInt64(2)) // 7 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(4).Quo(NewDecFromInt64(9)) // 4 / 9
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const minExp = -100_000
|
||||
sum, err = NewDecWithExp(1, minExp).Quo(NewDecFromInt64(10)) // 1e-100000 / 10
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = NewDecFromInt64(1).Quo(NewDecFromInt64(0)) // 1 / 0 -> error
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
// Output: 3.000000000000000000000000000000000
|
||||
// 3.500000000000000000000000000000000
|
||||
// 0.4444444444444444444444444444444444
|
||||
// exponent out of range: invalid decimal
|
||||
// division by zero: invalid decimal
|
||||
}
|
||||
|
||||
func ExampleDec_QuoExact() {
|
||||
sum, err := NewDecFromInt64(6).QuoExact(NewDecFromInt64(2)) // 6 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(7).QuoExact(NewDecFromInt64(2)) // 7 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(4).QuoExact(NewDecFromInt64(9)) // 4 / 9 -> error
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
const minExp = -100_000
|
||||
sum, err = NewDecWithExp(1, minExp).QuoExact(NewDecFromInt64(10)) // 1e-100000 / 10 -> error
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = NewDecFromInt64(1).QuoExact(NewDecFromInt64(0)) // 1 / 0 -> error
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
// Output: 3.000000000000000000000000000000000
|
||||
// 3.500000000000000000000000000000000
|
||||
// unexpected rounding
|
||||
// exponent out of range: invalid decimal
|
||||
// division by zero: invalid decimal
|
||||
}
|
||||
|
||||
func ExampleDec_QuoInteger() {
|
||||
sum, err := NewDecFromInt64(6).QuoInteger(NewDecFromInt64(2)) // 6 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(7).QuoInteger(NewDecFromInt64(2)) // 7 / 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(4).QuoInteger(NewDecFromInt64(9)) // 4 / 9 -> error
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const minExp = -100_000
|
||||
sum, err = NewDecWithExp(1, minExp).QuoInteger(NewDecFromInt64(10)) // 1e-100000 / 10 -> 0
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(1).QuoInteger(NewDecFromInt64(0)) // 1 / 0 -> error
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
// Output: 3
|
||||
// 3
|
||||
// 0
|
||||
// 0
|
||||
// division by zero: invalid decimal
|
||||
}
|
||||
|
||||
func ExampleDec_Mul() {
|
||||
sum, err := NewDecFromInt64(2).Mul(NewDecFromInt64(3)) // 2 * 3
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecWithExp(125, -2).Mul(NewDecFromInt64(2)) // 1.25 * 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const maxExp = 100_000
|
||||
sum, err = NewDecWithExp(1, maxExp).Mul(NewDecFromInt64(10)) // 1e100000 * 10 -> err
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = NewDecFromInt64(1).Mul(NewDecFromInt64(0)) // 1 * 0
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
// Output: 6
|
||||
// 2.50
|
||||
// exponent out of range: invalid decimal
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleDec_MulExact() {
|
||||
sum, err := NewDecFromInt64(2).MulExact(NewDecFromInt64(3)) // 2 * 3
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecWithExp(125, -2).MulExact(NewDecFromInt64(2)) // 1.25 * 2
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
const maxExp = 100_000
|
||||
sum, err = NewDecWithExp(1, maxExp).MulExact(NewDecFromInt64(10)) // 1e100000 * 10 -> err
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
a, err := NewDecFromString("0.12345678901234567890123456789012345") // 35 digits after the comma
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sum, err = a.MulExact(NewDecFromInt64(1))
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
|
||||
sum, err = a.MulExact(NewDecFromInt64(0))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
sum, err = NewDecFromInt64(1).MulExact(NewDecFromInt64(0)) // 1 * 0
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
// Output: 6
|
||||
// 2.50
|
||||
// exponent out of range: invalid decimal
|
||||
// unexpected rounding
|
||||
// 0E-35
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleDec_Modulo() {
|
||||
sum, err := NewDecFromInt64(7).Modulo(NewDecFromInt64(3)) // 7 mod 3 = 1
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(sum.String())
|
||||
|
||||
// Output: 1
|
||||
}
|
||||
527
math/dec_rapid_test.go
Normal file
527
math/dec_rapid_test.go
Normal file
@ -0,0 +1,527 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"pgregory.net/rapid"
|
||||
)
|
||||
|
||||
// Rapid is a Go library for property-based testing.
|
||||
func TestDecWithRapid(t *testing.T) {
|
||||
// Property tests
|
||||
t.Run("TestNewDecFromInt64", rapid.MakeCheck(testDecInt64))
|
||||
|
||||
// Properties about *FromString functions
|
||||
t.Run("TestInvalidNewDecFromString", rapid.MakeCheck(testInvalidNewDecFromString))
|
||||
|
||||
// Properties about addition
|
||||
t.Run("TestAddLeftIdentity", rapid.MakeCheck(testAddLeftIdentity))
|
||||
t.Run("TestAddRightIdentity", rapid.MakeCheck(testAddRightIdentity))
|
||||
t.Run("TestAddCommutative", rapid.MakeCheck(testAddCommutative))
|
||||
t.Run("TestAddAssociative", rapid.MakeCheck(testAddAssociative))
|
||||
|
||||
// Properties about subtraction
|
||||
t.Run("TestSubRightIdentity", rapid.MakeCheck(testSubRightIdentity))
|
||||
t.Run("TestSubZero", rapid.MakeCheck(testSubZero))
|
||||
|
||||
// Properties about multiplication
|
||||
t.Run("TestMulLeftIdentity", rapid.MakeCheck(testMulLeftIdentity))
|
||||
t.Run("TestMulRightIdentity", rapid.MakeCheck(testMulRightIdentity))
|
||||
t.Run("TestMulCommutative", rapid.MakeCheck(testMulCommutative))
|
||||
t.Run("TestMulAssociative", rapid.MakeCheck(testMulAssociative))
|
||||
t.Run("TestZeroIdentity", rapid.MakeCheck(testMulZero))
|
||||
|
||||
// Properties about division
|
||||
t.Run("TestDivisionBySelf", rapid.MakeCheck(testSelfQuo))
|
||||
t.Run("TestDivisionByOne", rapid.MakeCheck(testQuoByOne))
|
||||
|
||||
// Properties combining operations
|
||||
t.Run("TestSubAdd", rapid.MakeCheck(testSubAdd))
|
||||
t.Run("TestAddSub", rapid.MakeCheck(testAddSub))
|
||||
t.Run("TestMulQuoA", rapid.MakeCheck(testMulQuoA))
|
||||
t.Run("TestMulQuoB", rapid.MakeCheck(testMulQuoB))
|
||||
t.Run("TestMulQuoExact", rapid.MakeCheck(testMulQuoExact))
|
||||
t.Run("TestQuoMulExact", rapid.MakeCheck(testQuoMulExact))
|
||||
|
||||
// Properties about comparison and equality
|
||||
t.Run("TestCmpInverse", rapid.MakeCheck(testCmpInverse))
|
||||
t.Run("TestEqualCommutative", rapid.MakeCheck(testEqualCommutative))
|
||||
|
||||
// Properties about tests on a single Dec
|
||||
t.Run("TestIsZero", rapid.MakeCheck(testIsZero))
|
||||
t.Run("TestIsNegative", rapid.MakeCheck(testIsNegative))
|
||||
t.Run("TestIsPositive", rapid.MakeCheck(testIsPositive))
|
||||
t.Run("TestNumDecimalPlaces", rapid.MakeCheck(testNumDecimalPlaces))
|
||||
|
||||
// Unit tests
|
||||
zero := Dec{}
|
||||
one := NewDecFromInt64(1)
|
||||
two := NewDecFromInt64(2)
|
||||
three := NewDecFromInt64(3)
|
||||
four := NewDecFromInt64(4)
|
||||
five := NewDecFromInt64(5)
|
||||
minusOne := NewDecFromInt64(-1)
|
||||
|
||||
onePointOneFive, err := NewDecFromString("1.15")
|
||||
require.NoError(t, err)
|
||||
twoPointThreeFour, err := NewDecFromString("2.34")
|
||||
require.NoError(t, err)
|
||||
threePointFourNine, err := NewDecFromString("3.49")
|
||||
require.NoError(t, err)
|
||||
onePointFourNine, err := NewDecFromString("1.49")
|
||||
require.NoError(t, err)
|
||||
minusFivePointZero, err := NewDecFromString("-5.0")
|
||||
require.NoError(t, err)
|
||||
|
||||
twoThousand := NewDecWithExp(2, 3)
|
||||
require.True(t, twoThousand.Equal(NewDecFromInt64(2000)))
|
||||
|
||||
res, err := two.Add(zero)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(two))
|
||||
|
||||
res, err = five.Sub(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(three))
|
||||
|
||||
res, err = four.Quo(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(two))
|
||||
|
||||
res, err = five.QuoInteger(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(two))
|
||||
|
||||
res, err = five.Modulo(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(one))
|
||||
|
||||
x, err := four.Int64()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(4), x)
|
||||
|
||||
require.Equal(t, "5", five.String())
|
||||
|
||||
res, err = onePointOneFive.Add(twoPointThreeFour)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(threePointFourNine))
|
||||
|
||||
res, err = threePointFourNine.Sub(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(onePointFourNine))
|
||||
|
||||
res, err = minusOne.Sub(four)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(minusFivePointZero))
|
||||
|
||||
require.True(t, zero.IsZero())
|
||||
require.False(t, zero.IsPositive())
|
||||
require.False(t, zero.IsNegative())
|
||||
|
||||
require.False(t, one.IsZero())
|
||||
require.True(t, one.IsPositive())
|
||||
require.False(t, one.IsNegative())
|
||||
|
||||
require.False(t, minusOne.IsZero())
|
||||
require.False(t, minusOne.IsPositive())
|
||||
require.True(t, minusOne.IsNegative())
|
||||
|
||||
res, err = one.MulExact(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Equal(two))
|
||||
}
|
||||
|
||||
var genDec *rapid.Generator[Dec] = rapid.Custom(func(t *rapid.T) Dec {
|
||||
f := rapid.Float64().Draw(t, "f")
|
||||
dec, err := NewDecFromString(fmt.Sprintf("%g", f))
|
||||
require.NoError(t, err)
|
||||
return dec
|
||||
})
|
||||
|
||||
// A Dec value and the float used to create it
|
||||
type floatAndDec struct {
|
||||
float float64
|
||||
dec Dec
|
||||
}
|
||||
|
||||
// Generate a Dec value along with the float used to create it
|
||||
var genFloatAndDec *rapid.Generator[floatAndDec] = rapid.Custom(func(t *rapid.T) floatAndDec {
|
||||
f := rapid.Float64().Draw(t, "f")
|
||||
dec, err := NewDecFromString(fmt.Sprintf("%g", f))
|
||||
require.NoError(t, err)
|
||||
return floatAndDec{f, dec}
|
||||
})
|
||||
|
||||
// Property: n == NewDecFromInt64(n).Int64()
|
||||
func testDecInt64(t *rapid.T) {
|
||||
nIn := rapid.Int64().Draw(t, "n")
|
||||
nOut, err := NewDecFromInt64(nIn).Int64()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nIn, nOut)
|
||||
}
|
||||
|
||||
// Property: invalid_number_string(s) => NewDecFromString(s) == err
|
||||
func testInvalidNewDecFromString(t *rapid.T) {
|
||||
s := rapid.StringMatching("[[:alpha:]]+").Draw(t, "s")
|
||||
_, err := NewDecFromString(s)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// Property: 0 + a == a
|
||||
func testAddLeftIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := zero.Add(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a + 0 == a
|
||||
func testAddRightIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Add(zero)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a + b == b + a
|
||||
func testAddCommutative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
c, err := a.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := b.Add(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, c.Equal(d))
|
||||
}
|
||||
|
||||
// Property: (a + b) + c == a + (b + c)
|
||||
func testAddAssociative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
c := genDec.Draw(t, "c")
|
||||
|
||||
// (a + b) + c
|
||||
d, err := a.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
e, err := d.Add(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
// a + (b + c)
|
||||
f, err := b.Add(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
g, err := a.Add(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, e.Equal(g))
|
||||
}
|
||||
|
||||
// Property: a - 0 == a
|
||||
func testSubRightIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Sub(zero)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a - a == 0
|
||||
func testSubZero(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Sub(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, b.Equal(zero))
|
||||
}
|
||||
|
||||
// Property: 1 * a == a
|
||||
func testMulLeftIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
one := NewDecFromInt64(1)
|
||||
|
||||
b, err := one.Mul(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a * 1 == a
|
||||
func testMulRightIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
one := NewDecFromInt64(1)
|
||||
|
||||
b, err := a.Mul(one)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a * b == b * a
|
||||
func testMulCommutative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
c, err := a.Mul(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := b.Mul(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, c.Equal(d))
|
||||
}
|
||||
|
||||
// Property: (a * b) * c == a * (b * c)
|
||||
func testMulAssociative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
c := genDec.Draw(t, "c")
|
||||
|
||||
// (a * b) * c
|
||||
d, err := a.Mul(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
e, err := d.Mul(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
// a * (b * c)
|
||||
f, err := b.Mul(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
g, err := a.Mul(f)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, e.Equal(g))
|
||||
}
|
||||
|
||||
// Property: (a - b) + b == a
|
||||
func testSubAdd(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
c, err := a.Sub(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(d))
|
||||
}
|
||||
|
||||
// Property: (a + b) - b == a
|
||||
func testAddSub(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
c, err := a.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Sub(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(d))
|
||||
}
|
||||
|
||||
// Property: a * 0 = 0
|
||||
func testMulZero(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
zero := Dec{}
|
||||
|
||||
c, err := a.Mul(zero)
|
||||
require.NoError(t, err)
|
||||
require.True(t, c.IsZero())
|
||||
}
|
||||
|
||||
// Property: a/a = 1
|
||||
func testSelfQuo(t *rapid.T) {
|
||||
decNotZero := func(d Dec) bool { return !d.IsZero() }
|
||||
a := genDec.Filter(decNotZero).Draw(t, "a")
|
||||
one := NewDecFromInt64(1)
|
||||
|
||||
b, err := a.Quo(a)
|
||||
require.NoError(t, err)
|
||||
require.True(t, one.Equal(b))
|
||||
}
|
||||
|
||||
// Property: a/1 = a
|
||||
func testQuoByOne(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
one := NewDecFromInt64(1)
|
||||
|
||||
b, err := a.Quo(one)
|
||||
require.NoError(t, err)
|
||||
require.True(t, a.Equal(b))
|
||||
}
|
||||
|
||||
// Property: (a * b) / a == b
|
||||
func testMulQuoA(t *rapid.T) {
|
||||
decNotZero := func(d Dec) bool { return !d.IsZero() }
|
||||
a := genDec.Filter(decNotZero).Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
c, err := a.Mul(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Quo(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, b.Equal(d))
|
||||
}
|
||||
|
||||
// Property: (a * b) / b == a
|
||||
func testMulQuoB(t *rapid.T) {
|
||||
decNotZero := func(d Dec) bool { return !d.IsZero() }
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Filter(decNotZero).Draw(t, "b")
|
||||
|
||||
c, err := a.Mul(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Quo(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(d))
|
||||
}
|
||||
|
||||
// Property: (a * 10^b) / 10^b == a using MulExact and QuoExact
|
||||
// and a with no more than b decimal places (b <= 32).
|
||||
func testMulQuoExact(t *rapid.T) {
|
||||
b := rapid.Uint32Range(0, 32).Draw(t, "b")
|
||||
decPrec := func(d Dec) bool { return d.NumDecimalPlaces() <= b }
|
||||
a := genDec.Filter(decPrec).Draw(t, "a")
|
||||
|
||||
c := NewDecWithExp(1, int32(b))
|
||||
|
||||
d, err := a.MulExact(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
e, err := d.QuoExact(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.Equal(e))
|
||||
}
|
||||
|
||||
// Property: (a / b) * b == a using QuoExact and MulExact and
|
||||
// a as an integer.
|
||||
func testQuoMulExact(t *rapid.T) {
|
||||
a := rapid.Uint64().Draw(t, "a")
|
||||
aDec, err := NewDecFromString(fmt.Sprintf("%d", a))
|
||||
require.NoError(t, err)
|
||||
b := rapid.Uint32Range(0, 32).Draw(t, "b")
|
||||
c := NewDecWithExp(1, int32(b))
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := aDec.QuoExact(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
e, err := d.MulExact(c)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, aDec.Equal(e))
|
||||
}
|
||||
|
||||
// Property: Cmp(a, b) == -Cmp(b, a)
|
||||
func testCmpInverse(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
require.Equal(t, a.Cmp(b), -b.Cmp(a))
|
||||
}
|
||||
|
||||
// Property: Equal(a, b) == Equal(b, a)
|
||||
func testEqualCommutative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a")
|
||||
b := genDec.Draw(t, "b")
|
||||
|
||||
require.Equal(t, a.Equal(b), b.Equal(a))
|
||||
}
|
||||
|
||||
// Property: isZero(f) == isZero(NewDecFromString(f.String()))
|
||||
func testIsZero(t *rapid.T) {
|
||||
floatAndDec := genFloatAndDec.Draw(t, "floatAndDec")
|
||||
f, dec := floatAndDec.float, floatAndDec.dec
|
||||
|
||||
require.Equal(t, f == 0, dec.IsZero())
|
||||
}
|
||||
|
||||
// Property: isNegative(f) == isNegative(NewDecFromString(f.String()))
|
||||
func testIsNegative(t *rapid.T) {
|
||||
floatAndDec := genFloatAndDec.Draw(t, "floatAndDec")
|
||||
f, dec := floatAndDec.float, floatAndDec.dec
|
||||
|
||||
require.Equal(t, f < 0, dec.IsNegative())
|
||||
}
|
||||
|
||||
// Property: isPositive(f) == isPositive(NewDecFromString(f.String()))
|
||||
func testIsPositive(t *rapid.T) {
|
||||
floatAndDec := genFloatAndDec.Draw(t, "floatAndDec")
|
||||
f, dec := floatAndDec.float, floatAndDec.dec
|
||||
|
||||
require.Equal(t, f > 0, dec.IsPositive())
|
||||
}
|
||||
|
||||
// Property: floatDecimalPlaces(f) == NumDecimalPlaces(NewDecFromString(f.String()))
|
||||
func testNumDecimalPlaces(t *rapid.T) {
|
||||
floatAndDec := genFloatAndDec.Draw(t, "floatAndDec")
|
||||
f, dec := floatAndDec.float, floatAndDec.dec
|
||||
|
||||
require.Equal(t, floatDecimalPlaces(t, f), dec.NumDecimalPlaces())
|
||||
}
|
||||
|
||||
func floatDecimalPlaces(t *rapid.T, f float64) uint32 {
|
||||
reScientific := regexp.MustCompile(`^\-?(?:[[:digit:]]+(?:\.([[:digit:]]+))?|\.([[:digit:]]+))(?:e?(?:\+?([[:digit:]]+)|(-[[:digit:]]+)))?$`)
|
||||
fStr := fmt.Sprintf("%g", f)
|
||||
matches := reScientific.FindAllStringSubmatch(fStr, 1)
|
||||
if len(matches) != 1 {
|
||||
t.Fatalf("Didn't match float: %g", f)
|
||||
}
|
||||
|
||||
// basePlaces is the number of decimal places in the decimal part of the
|
||||
// string
|
||||
basePlaces := 0
|
||||
if matches[0][1] != "" {
|
||||
basePlaces = len(matches[0][1])
|
||||
} else if matches[0][2] != "" {
|
||||
basePlaces = len(matches[0][2])
|
||||
}
|
||||
t.Logf("Base places: %d", basePlaces)
|
||||
|
||||
// exp is the exponent
|
||||
exp := 0
|
||||
if matches[0][3] != "" {
|
||||
var err error
|
||||
exp, err = strconv.Atoi(matches[0][3])
|
||||
require.NoError(t, err)
|
||||
} else if matches[0][4] != "" {
|
||||
var err error
|
||||
exp, err = strconv.Atoi(matches[0][4])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Subtract exponent from base and check if negative
|
||||
res := basePlaces - exp
|
||||
if res <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return uint32(res)
|
||||
}
|
||||
2101
math/dec_test.go
2101
math/dec_test.go
File diff suppressed because it is too large
Load Diff
38
math/go.mod
38
math/go.mod
@ -1,17 +1,39 @@
|
||||
module cosmossdk.io/math
|
||||
|
||||
go 1.23
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
github.com/cockroachdb/apd/v3 v3.2.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/errors v1.0.1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
pgregory.net/rapid v1.1.0
|
||||
)
|
||||
|
||||
// Issue with math.Int{}.Size() implementation.
|
||||
retract [v1.1.0, v1.1.1]
|
||||
|
||||
// Bit length differences between Int and Dec
|
||||
retract (
|
||||
v1.3.0
|
||||
v1.2.0
|
||||
v1.1.2
|
||||
[v1.0.0, v1.0.1]
|
||||
)
|
||||
|
||||
51
math/go.sum
51
math/go.sum
@ -1,26 +1,49 @@
|
||||
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=
|
||||
cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 h1:yrTuav+chrF0zF/joFGICKTzYv7mh/gr9AgEXrVU8ao=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
|
||||
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
174
math/int.go
174
math/int.go
@ -3,8 +3,10 @@ package math
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@ -13,6 +15,20 @@ import (
|
||||
// MaxBitLen defines the maximum bit length supported bit Int and Uint types.
|
||||
const MaxBitLen = 256
|
||||
|
||||
// maxWordLen defines the maximum word length supported by Int and Uint types.
|
||||
// We check overflow, by first doing a fast check if the word length is below maxWordLen
|
||||
// and if not then do the slower full bitlen check.
|
||||
// NOTE: If MaxBitLen is not a multiple of bits.UintSize, then we need to edit the used logic slightly.
|
||||
const maxWordLen = MaxBitLen / bits.UintSize
|
||||
|
||||
// Integer errors
|
||||
var (
|
||||
// ErrIntOverflow is the error returned when an integer overflow occurs
|
||||
ErrIntOverflow = errors.New("integer overflow")
|
||||
// ErrDivideByZero is the error returned when a divide by zero occurs
|
||||
ErrDivideByZero = errors.New("divide by zero")
|
||||
)
|
||||
|
||||
func newIntegerFromString(s string) (*big.Int, bool) {
|
||||
return new(big.Int).SetString(s, 0)
|
||||
}
|
||||
@ -62,7 +78,7 @@ func unmarshalText(i *big.Int, text string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.BitLen() > MaxBitLen {
|
||||
if bigIntOverflows(i) {
|
||||
return fmt.Errorf("integer out of range: %s", text)
|
||||
}
|
||||
|
||||
@ -71,7 +87,7 @@ func unmarshalText(i *big.Int, text string) error {
|
||||
|
||||
var _ customProtobufType = (*Int)(nil)
|
||||
|
||||
// Int wraps big.Int with a 257 bit range bound
|
||||
// Int wraps big.Int with a 256 bit range bound
|
||||
// Checks overflow, underflow and division by zero
|
||||
// Exists in range from -(2^256 - 1) to 2^256 - 1
|
||||
type Int struct {
|
||||
@ -86,6 +102,14 @@ func (i Int) BigInt() *big.Int {
|
||||
return new(big.Int).Set(i.i)
|
||||
}
|
||||
|
||||
// BigIntMut converts Int to big.Int, mutative the input
|
||||
func (i Int) BigIntMut() *big.Int {
|
||||
if i.IsNil() {
|
||||
return nil
|
||||
}
|
||||
return i.i
|
||||
}
|
||||
|
||||
// IsNil returns true if Int is uninitialized
|
||||
func (i Int) IsNil() bool {
|
||||
return i.i == nil
|
||||
@ -105,14 +129,31 @@ func NewIntFromUint64(n uint64) Int {
|
||||
|
||||
// NewIntFromBigInt constructs Int from big.Int. If the provided big.Int is nil,
|
||||
// it returns an empty instance. This function panics if the bit length is > 256.
|
||||
// Note, the caller can safely mutate the argument after this function returns.
|
||||
func NewIntFromBigInt(i *big.Int) Int {
|
||||
if i == nil {
|
||||
return Int{}
|
||||
}
|
||||
|
||||
if i.BitLen() > MaxBitLen {
|
||||
if bigIntOverflows(i) {
|
||||
panic("NewIntFromBigInt() out of bound")
|
||||
}
|
||||
|
||||
return Int{new(big.Int).Set(i)}
|
||||
}
|
||||
|
||||
// NewIntFromBigIntMut constructs Int from big.Int. If the provided big.Int is nil,
|
||||
// it returns an empty instance. This function panics if the bit length is > 256.
|
||||
// Note, this function mutate the argument.
|
||||
func NewIntFromBigIntMut(i *big.Int) Int {
|
||||
if i == nil {
|
||||
return Int{}
|
||||
}
|
||||
|
||||
if bigIntOverflows(i) {
|
||||
panic("NewIntFromBigInt() out of bound")
|
||||
}
|
||||
|
||||
return Int{i}
|
||||
}
|
||||
|
||||
@ -123,7 +164,7 @@ func NewIntFromString(s string) (res Int, ok bool) {
|
||||
return
|
||||
}
|
||||
// Check overflow
|
||||
if i.BitLen() > MaxBitLen {
|
||||
if bigIntOverflows(i) {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
@ -141,7 +182,7 @@ func NewIntWithDecimal(n int64, dec int) Int {
|
||||
i.Mul(big.NewInt(n), exp)
|
||||
|
||||
// Check overflow
|
||||
if i.BitLen() > MaxBitLen {
|
||||
if bigIntOverflows(i) {
|
||||
panic("NewIntWithDecimal() out of bound")
|
||||
}
|
||||
return Int{i}
|
||||
@ -153,6 +194,11 @@ func ZeroInt() Int { return Int{big.NewInt(0)} }
|
||||
// OneInt returns Int value with one
|
||||
func OneInt() Int { return Int{big.NewInt(1)} }
|
||||
|
||||
// ToLegacyDec converts Int to LegacyDec
|
||||
func (i Int) ToLegacyDec() LegacyDec {
|
||||
return LegacyNewDecFromInt(i)
|
||||
}
|
||||
|
||||
// Int64 converts Int to int64
|
||||
// Panics if the value is out of range
|
||||
func (i Int) Int64() int64 {
|
||||
@ -229,12 +275,12 @@ func (i Int) LTE(i2 Int) bool {
|
||||
|
||||
// Add adds Int from another
|
||||
func (i Int) Add(i2 Int) (res Int) {
|
||||
res = Int{add(i.i, i2.i)}
|
||||
// Check overflow
|
||||
if res.i.BitLen() > MaxBitLen {
|
||||
panic("Int overflow")
|
||||
x, err := i.SafeAdd(i2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
return x
|
||||
}
|
||||
|
||||
// AddRaw adds int64 to Int
|
||||
@ -242,14 +288,24 @@ func (i Int) AddRaw(i2 int64) Int {
|
||||
return i.Add(NewInt(i2))
|
||||
}
|
||||
|
||||
// SafeAdd adds Int from another and returns an error if overflow
|
||||
func (i Int) SafeAdd(i2 Int) (res Int, err error) {
|
||||
res = Int{add(i.i, i2.i)}
|
||||
// Check overflow
|
||||
if bigIntOverflows(res.i) {
|
||||
return Int{}, ErrIntOverflow
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Sub subtracts Int from another
|
||||
func (i Int) Sub(i2 Int) (res Int) {
|
||||
res = Int{sub(i.i, i2.i)}
|
||||
// Check overflow
|
||||
if res.i.BitLen() > MaxBitLen {
|
||||
panic("Int overflow")
|
||||
x, err := i.SafeSub(i2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
return x
|
||||
}
|
||||
|
||||
// SubRaw subtracts int64 from Int
|
||||
@ -257,32 +313,49 @@ func (i Int) SubRaw(i2 int64) Int {
|
||||
return i.Sub(NewInt(i2))
|
||||
}
|
||||
|
||||
// SafeSub subtracts Int from another and returns an error if overflow or underflow
|
||||
func (i Int) SafeSub(i2 Int) (res Int, err error) {
|
||||
res = Int{sub(i.i, i2.i)}
|
||||
// Check overflow/underflow
|
||||
if bigIntOverflows(res.i) {
|
||||
return Int{}, ErrIntOverflow
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Mul multiples two Ints
|
||||
func (i Int) Mul(i2 Int) (res Int) {
|
||||
// Check overflow
|
||||
if i.i.BitLen()+i2.i.BitLen()-1 > MaxBitLen {
|
||||
panic("Int overflow")
|
||||
x, err := i.SafeMul(i2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res = Int{mul(i.i, i2.i)}
|
||||
// Check overflow if sign of both are same
|
||||
if res.i.BitLen() > MaxBitLen {
|
||||
panic("Int overflow")
|
||||
}
|
||||
return
|
||||
return x
|
||||
}
|
||||
|
||||
// MulRaw multipies Int and int64
|
||||
// MulRaw multiplies Int and int64
|
||||
func (i Int) MulRaw(i2 int64) Int {
|
||||
return i.Mul(NewInt(i2))
|
||||
}
|
||||
|
||||
// SafeMul multiples Int from another and returns an error if overflow
|
||||
func (i Int) SafeMul(i2 Int) (res Int, err error) {
|
||||
res = Int{mul(i.i, i2.i)}
|
||||
// Check overflow
|
||||
if bigIntOverflows(res.i) {
|
||||
return Int{}, ErrIntOverflow
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Quo divides Int with Int
|
||||
func (i Int) Quo(i2 Int) (res Int) {
|
||||
// Check division-by-zero
|
||||
if i2.i.Sign() == 0 {
|
||||
x, err := i.SafeQuo(i2)
|
||||
if err != nil {
|
||||
panic("Division by zero")
|
||||
}
|
||||
return Int{div(i.i, i2.i)}
|
||||
return x
|
||||
}
|
||||
|
||||
// QuoRaw divides Int with int64
|
||||
@ -290,12 +363,22 @@ func (i Int) QuoRaw(i2 int64) Int {
|
||||
return i.Quo(NewInt(i2))
|
||||
}
|
||||
|
||||
// SafeQuo divides Int with Int and returns an error if division by zero
|
||||
func (i Int) SafeQuo(i2 Int) (res Int, err error) {
|
||||
// Check division-by-zero
|
||||
if i2.i.Sign() == 0 {
|
||||
return Int{}, ErrDivideByZero
|
||||
}
|
||||
return Int{div(i.i, i2.i)}, nil
|
||||
}
|
||||
|
||||
// Mod returns remainder after dividing with Int
|
||||
func (i Int) Mod(i2 Int) Int {
|
||||
if i2.Sign() == 0 {
|
||||
panic("division-by-zero")
|
||||
x, err := i.SafeMod(i2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Int{mod(i.i, i2.i)}
|
||||
return x
|
||||
}
|
||||
|
||||
// ModRaw returns remainder after dividing with int64
|
||||
@ -303,6 +386,14 @@ func (i Int) ModRaw(i2 int64) Int {
|
||||
return i.Mod(NewInt(i2))
|
||||
}
|
||||
|
||||
// SafeMod returns remainder after dividing with Int and returns an error if division by zero
|
||||
func (i Int) SafeMod(i2 Int) (res Int, err error) {
|
||||
if i2.Sign() == 0 {
|
||||
return Int{}, ErrDivideByZero
|
||||
}
|
||||
return Int{mod(i.i, i2.i)}, nil
|
||||
}
|
||||
|
||||
// Neg negates Int
|
||||
func (i Int) Neg() (res Int) {
|
||||
return Int{neg(i.i)}
|
||||
@ -313,7 +404,7 @@ func (i Int) Abs() Int {
|
||||
return Int{abs(i.i)}
|
||||
}
|
||||
|
||||
// return the minimum of the ints
|
||||
// MinInt return the minimum of the ints
|
||||
func MinInt(i1, i2 Int) Int {
|
||||
return Int{min(i1.BigInt(), i2.BigInt())}
|
||||
}
|
||||
@ -323,7 +414,7 @@ func MaxInt(i, i2 Int) Int {
|
||||
return Int{max(i.BigInt(), i2.BigInt())}
|
||||
}
|
||||
|
||||
// Human readable string
|
||||
// String returns human-readable string
|
||||
func (i Int) String() string {
|
||||
return i.i.String()
|
||||
}
|
||||
@ -344,7 +435,7 @@ func (i *Int) UnmarshalJSON(bz []byte) error {
|
||||
return unmarshalJSON(i.i, bz)
|
||||
}
|
||||
|
||||
// MarshalJSON for custom encoding scheme
|
||||
// marshalJSON for custom encoding scheme
|
||||
// Must be encoded as a string for JSON precision
|
||||
func marshalJSON(i encoding.TextMarshaler) ([]byte, error) {
|
||||
text, err := i.MarshalText()
|
||||
@ -355,7 +446,7 @@ func marshalJSON(i encoding.TextMarshaler) ([]byte, error) {
|
||||
return json.Marshal(string(text))
|
||||
}
|
||||
|
||||
// UnmarshalJSON for custom decoding scheme
|
||||
// unmarshalJSON for custom decoding scheme
|
||||
// Must be encoded as a string for JSON precision
|
||||
func unmarshalJSON(i *big.Int, bz []byte) error {
|
||||
var text string
|
||||
@ -413,7 +504,7 @@ func (i *Int) Unmarshal(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.i.BitLen() > MaxBitLen {
|
||||
if bigIntOverflows(i.i) {
|
||||
return fmt.Errorf("integer out of range; got: %d, max: %d", i.i.BitLen(), MaxBitLen)
|
||||
}
|
||||
|
||||
@ -426,12 +517,13 @@ func (i *Int) Size() int {
|
||||
return len(bz)
|
||||
}
|
||||
|
||||
// Override Amino binary serialization by proxying to protobuf.
|
||||
// MarshalAmino Override Amino binary serialization by proxying to protobuf.
|
||||
func (i Int) MarshalAmino() ([]byte, error) { return i.Marshal() }
|
||||
func (i *Int) UnmarshalAmino(bz []byte) error { return i.Unmarshal(bz) }
|
||||
|
||||
// intended to be used with require/assert: require.True(IntEq(...))
|
||||
// IntEq intended to be used with require/assert: require.True(IntEq(...))
|
||||
func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) {
|
||||
t.Helper()
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
|
||||
}
|
||||
|
||||
@ -458,7 +550,7 @@ var stringsBuilderPool = &sync.Pool{
|
||||
// (instead of manipulating the int or math.Int object).
|
||||
func FormatInt(v string) (string, error) {
|
||||
if len(v) == 0 {
|
||||
return "", fmt.Errorf("cannot format empty string")
|
||||
return "", errors.New("cannot format empty string")
|
||||
}
|
||||
|
||||
sign := ""
|
||||
@ -506,3 +598,15 @@ func FormatInt(v string) (string, error) {
|
||||
|
||||
return sign + sb.String(), nil
|
||||
}
|
||||
|
||||
// check if the big int overflows.
|
||||
func bigIntOverflows(i *big.Int) bool {
|
||||
// overflow is defined as i.BitLen() > MaxBitLen
|
||||
// however this check can be expensive when doing many operations.
|
||||
// So we first check if the word length is greater than maxWordLen.
|
||||
// However the most significant word could be zero, hence we still do the bitlen check.
|
||||
if len(i.Bits()) > maxWordLen {
|
||||
return i.BitLen() > MaxBitLen
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
201
math/int_test.go
201
math/int_test.go
@ -43,6 +43,57 @@ func (s *intTestSuite) TestFromUint64() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *intTestSuite) TestNewIntFromBigInt() {
|
||||
i := math.NewIntFromBigInt(nil)
|
||||
s.Require().True(i.IsNil())
|
||||
|
||||
r := big.NewInt(42)
|
||||
i = math.NewIntFromBigInt(r)
|
||||
s.Require().Equal(r, i.BigInt())
|
||||
|
||||
// modify r and ensure i doesn't change
|
||||
r = r.SetInt64(100)
|
||||
s.Require().NotEqual(r, i.BigInt())
|
||||
}
|
||||
|
||||
func (s *intTestSuite) TestNewIntFromBigIntMut() {
|
||||
im := math.NewIntFromBigIntMut(nil)
|
||||
s.Require().True(im.IsNil())
|
||||
|
||||
r := big.NewInt(42)
|
||||
im = math.NewIntFromBigIntMut(r)
|
||||
s.Require().Equal(r, im.BigInt())
|
||||
|
||||
// Compare value of NewIntFromBigInt and NewIntFromBigIntMut
|
||||
i := math.NewIntFromBigInt(r)
|
||||
s.Require().Equal(i, im)
|
||||
|
||||
// modify r and ensure i doesn't change & im changes
|
||||
r = r.SetInt64(100)
|
||||
s.Require().NotEqual(r, i.BigInt())
|
||||
s.Require().Equal(r, im.BigInt())
|
||||
}
|
||||
|
||||
func (s *intTestSuite) TestConvertToBigIntMutative() {
|
||||
r := big.NewInt(42)
|
||||
i := math.NewIntFromBigInt(r)
|
||||
|
||||
// Compare value of BigInt & BigIntMut
|
||||
s.Require().Equal(i.BigInt(), i.BigIntMut())
|
||||
|
||||
// Modify BigIntMut() pointer and ensure i.BigIntMut() & i.BigInt() change
|
||||
p := i.BigIntMut()
|
||||
p.SetInt64(50)
|
||||
s.Require().Equal(big.NewInt(50), i.BigIntMut())
|
||||
s.Require().Equal(big.NewInt(50), i.BigInt())
|
||||
|
||||
// Modify big.Int() pointer and ensure i.BigIntMut() & i.BigInt() don't change
|
||||
p = i.BigInt()
|
||||
p.SetInt64(60)
|
||||
s.Require().NotEqual(big.NewInt(60), i.BigIntMut())
|
||||
s.Require().NotEqual(big.NewInt(60), i.BigInt())
|
||||
}
|
||||
|
||||
func (s *intTestSuite) TestIntPanic() {
|
||||
// Max Int = 2^256-1 = 1.1579209e+77
|
||||
// Min Int = -(2^256-1) = -1.1579209e+77
|
||||
@ -60,32 +111,66 @@ func (s *intTestSuite) TestIntPanic() {
|
||||
s.Require().NotPanics(func() { i1.Add(i1) })
|
||||
s.Require().NotPanics(func() { i2.Add(i2) })
|
||||
s.Require().Panics(func() { i3.Add(i3) })
|
||||
_, err := i1.SafeAdd(i1)
|
||||
s.Require().Nil(err)
|
||||
_, err = i2.SafeAdd(i2)
|
||||
s.Require().Nil(err)
|
||||
_, err = i3.SafeAdd(i3)
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().NotPanics(func() { i1.Sub(i1.Neg()) })
|
||||
s.Require().NotPanics(func() { i2.Sub(i2.Neg()) })
|
||||
s.Require().Panics(func() { i3.Sub(i3.Neg()) })
|
||||
_, err = i1.SafeSub(i1.Neg())
|
||||
s.Require().Nil(err)
|
||||
_, err = i2.SafeSub(i2.Neg())
|
||||
s.Require().Nil(err)
|
||||
_, err = i3.SafeSub(i3.Neg())
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().Panics(func() { i1.Mul(i1) })
|
||||
s.Require().Panics(func() { i2.Mul(i2) })
|
||||
s.Require().Panics(func() { i3.Mul(i3) })
|
||||
_, err = i1.SafeMul(i1)
|
||||
s.Require().Error(err)
|
||||
_, err = i2.SafeMul(i2)
|
||||
s.Require().Error(err)
|
||||
_, err = i3.SafeMul(i3)
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().Panics(func() { i1.Neg().Mul(i1.Neg()) })
|
||||
s.Require().Panics(func() { i2.Neg().Mul(i2.Neg()) })
|
||||
s.Require().Panics(func() { i3.Neg().Mul(i3.Neg()) })
|
||||
_, err = i1.Neg().SafeMul(i1.Neg())
|
||||
s.Require().Error(err)
|
||||
_, err = i2.Neg().SafeMul(i2.Neg())
|
||||
s.Require().Error(err)
|
||||
_, err = i3.Neg().SafeMul(i3.Neg())
|
||||
s.Require().Error(err)
|
||||
|
||||
// // Underflow check
|
||||
// Underflow check
|
||||
i3n := i3.Neg()
|
||||
s.Require().NotPanics(func() { i3n.Sub(i1) })
|
||||
s.Require().NotPanics(func() { i3n.Sub(i2) })
|
||||
s.Require().Panics(func() { i3n.Sub(i3) })
|
||||
_, err = i3n.SafeSub(i3)
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().NotPanics(func() { i3n.Add(i1.Neg()) })
|
||||
s.Require().NotPanics(func() { i3n.Add(i2.Neg()) })
|
||||
s.Require().Panics(func() { i3n.Add(i3.Neg()) })
|
||||
_, err = i3n.SafeAdd(i3.Neg())
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().Panics(func() { i1.Mul(i1.Neg()) })
|
||||
s.Require().Panics(func() { i2.Mul(i2.Neg()) })
|
||||
s.Require().Panics(func() { i3.Mul(i3.Neg()) })
|
||||
_, err = i1.SafeMul(i1.Neg())
|
||||
s.Require().Error(err)
|
||||
_, err = i2.SafeMul(i2.Neg())
|
||||
s.Require().Error(err)
|
||||
_, err = i3.SafeMul(i3.Neg())
|
||||
s.Require().Error(err)
|
||||
|
||||
// Bound check
|
||||
intmax := math.NewIntFromBigInt(new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(1)))
|
||||
@ -94,12 +179,18 @@ func (s *intTestSuite) TestIntPanic() {
|
||||
s.Require().NotPanics(func() { intmin.Sub(math.ZeroInt()) })
|
||||
s.Require().Panics(func() { intmax.Add(math.OneInt()) })
|
||||
s.Require().Panics(func() { intmin.Sub(math.OneInt()) })
|
||||
_, err = intmax.SafeAdd(math.OneInt())
|
||||
s.Require().Error(err)
|
||||
_, err = intmin.SafeSub(math.OneInt())
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().NotPanics(func() { math.NewIntFromBigInt(nil) })
|
||||
s.Require().True(math.NewIntFromBigInt(nil).IsNil())
|
||||
|
||||
// Division-by-zero check
|
||||
s.Require().Panics(func() { i1.Quo(math.NewInt(0)) })
|
||||
_, err = i1.SafeQuo(math.NewInt(0))
|
||||
s.Require().Error(err)
|
||||
|
||||
s.Require().NotPanics(func() { math.Int{}.BigInt() })
|
||||
}
|
||||
@ -408,7 +499,6 @@ func TestRoundTripMarshalToInt(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
value := value
|
||||
t.Run(fmt.Sprintf("%d", value), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -457,7 +547,6 @@ func TestFormatIntNonDigits(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, value := range badCases {
|
||||
value := value
|
||||
t.Run(value, func(t *testing.T) {
|
||||
s, err := math.FormatInt(value)
|
||||
if err == nil {
|
||||
@ -500,7 +589,6 @@ func TestFormatIntCorrectness(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.in, func(t *testing.T) {
|
||||
got, err := math.FormatInt(tt.in)
|
||||
if err != nil {
|
||||
@ -513,3 +601,108 @@ func TestFormatIntCorrectness(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var sizeTests = []struct {
|
||||
s string
|
||||
want int
|
||||
}{
|
||||
{"", 1},
|
||||
{"0", 1},
|
||||
{"-0", 1},
|
||||
{"-10", 3},
|
||||
{"-10000", 6},
|
||||
{"10000", 5},
|
||||
{"100000", 6},
|
||||
{"99999", 5},
|
||||
{"9999999999", 10},
|
||||
{"10000000000", 11},
|
||||
{"99999999999", 11},
|
||||
{"999999999999", 12},
|
||||
{"9999999999999", 13},
|
||||
{"99999999999999", 14},
|
||||
{"999999999999999", 15},
|
||||
{"1000000000000000", 16},
|
||||
{"9999999999999999", 16},
|
||||
{"99999999999999999", 17},
|
||||
{"999999999999999999", 18},
|
||||
{"-999999999999999999", 19},
|
||||
{"9000000000000000000", 19},
|
||||
{"-9999999999999990000", 20},
|
||||
{"9999999999999990000", 19},
|
||||
{"9999999999999999000", 19},
|
||||
{"9999999999999999999", 19},
|
||||
{"-9999999999999999999", 20},
|
||||
{"18446744073709551616", 20},
|
||||
{"18446744073709551618", 20},
|
||||
{"184467440737095516181", 21},
|
||||
{"100000000000000000000000", 24},
|
||||
{"1000000000000000000000000000", 28},
|
||||
{"9000000000099999999999999999", 28},
|
||||
{"9999999999999999999999999999", 28},
|
||||
{"9903520314283042199192993792", 28},
|
||||
{"340282366920938463463374607431768211456", 39},
|
||||
{"3402823669209384634633746074317682114569999", 43},
|
||||
{"9999999999999999999999999999999999999999999", 43},
|
||||
{"99999999999999999999999999999999999999999999", 44},
|
||||
{"999999999999999999999999999999999999999999999", 45},
|
||||
{"90000000000999999999999999999000000000099999999999999999", 56},
|
||||
{"-90000000000999999999999999999000000000099999999999999999", 57},
|
||||
{"9000000000099999999999999999900000000009999999999999999990", 58},
|
||||
{"990000000009999999999999999990000000000999999999999999999999", 60},
|
||||
{"99000000000999999999999999999000000000099999999999999999999919", 62},
|
||||
{"90000000000999999990000000000000000000000000000000000000000000", 62},
|
||||
{"99999999999999999999999999990000000000000000000000000000000000", 62},
|
||||
{"11111111111111119999999999990000000000000000000000000000000000", 62},
|
||||
{"99000000000999999999999999999000000000099999999999999999999919", 62},
|
||||
{"10000000000000000000000000000000000000000000000000000000000000", 62},
|
||||
{"10000000000000000000000000000000000000000000000000000000000000000000000000000", 77},
|
||||
{"99999999999999999999999999999999999999999999999999999999999999999999999999999", 77},
|
||||
{"110000000000000000000000000000000000000000000000000000000000000000000000000009", 78},
|
||||
}
|
||||
|
||||
func TestNewIntFromString(t *testing.T) {
|
||||
for _, st := range sizeTests {
|
||||
ii, _ := math.NewIntFromString(st.s)
|
||||
require.Equal(t, st.want, ii.Size(), "size mismatch for %q", st.s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntSize(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, st := range sizeTests {
|
||||
ii, _ := math.NewIntFromString(st.s)
|
||||
got := ii.Size()
|
||||
if got != st.want {
|
||||
b.Errorf("%q:: got=%d, want=%d", st.s, got, st.want)
|
||||
}
|
||||
sink = got
|
||||
}
|
||||
}
|
||||
if sink == nil {
|
||||
b.Fatal("Benchmark did not run!")
|
||||
}
|
||||
sink = nil
|
||||
}
|
||||
|
||||
func BenchmarkIntOverflowCheckTime(b *testing.B) {
|
||||
ints := []*big.Int{}
|
||||
|
||||
for _, st := range sizeTests {
|
||||
ii, _ := math.NewIntFromString(st.s)
|
||||
ints = append(ints, ii.BigInt())
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := range sizeTests {
|
||||
got := math.NewIntFromBigIntMut(ints[j])
|
||||
sink = got
|
||||
}
|
||||
}
|
||||
if sink == nil {
|
||||
b.Fatal("Benchmark did not run!")
|
||||
}
|
||||
sink = nil
|
||||
}
|
||||
|
||||
971
math/legacy_dec.go
Normal file
971
math/legacy_dec.go
Normal file
@ -0,0 +1,971 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// LegacyDec NOTE: never use new(Dec) or else we will panic unmarshalling into the
|
||||
// nil embedded big.Int
|
||||
type LegacyDec struct {
|
||||
i *big.Int
|
||||
}
|
||||
|
||||
const (
|
||||
// LegacyPrecision number of decimal places
|
||||
LegacyPrecision = 18
|
||||
|
||||
// LegacyDecimalPrecisionBits bits required to represent the above precision
|
||||
// Ceiling[Log2[10^Precision - 1]]
|
||||
// Deprecated: This is unused and will be removed
|
||||
LegacyDecimalPrecisionBits = 60
|
||||
|
||||
// maxApproxRootIterations max number of iterations in ApproxRoot function
|
||||
maxApproxRootIterations = 300
|
||||
)
|
||||
|
||||
var (
|
||||
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(LegacyPrecision), nil)
|
||||
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2))
|
||||
|
||||
upperLimit LegacyDec
|
||||
lowerLimit LegacyDec
|
||||
|
||||
precisionMultipliers []*big.Int
|
||||
zeroInt = big.NewInt(0)
|
||||
oneInt = big.NewInt(1)
|
||||
tenInt = big.NewInt(10)
|
||||
smallestDec = LegacySmallestDec()
|
||||
)
|
||||
|
||||
// Decimal errors
|
||||
var (
|
||||
ErrLegacyEmptyDecimalStr = errors.New("decimal string cannot be empty")
|
||||
ErrLegacyInvalidDecimalLength = errors.New("invalid decimal length")
|
||||
ErrLegacyInvalidDecimalStr = errors.New("invalid decimal string")
|
||||
)
|
||||
|
||||
// Set precision multipliers
|
||||
func init() {
|
||||
precisionMultipliers = make([]*big.Int, LegacyPrecision+1)
|
||||
for i := 0; i <= LegacyPrecision; i++ {
|
||||
precisionMultipliers[i] = calcPrecisionMultiplier(int64(i))
|
||||
}
|
||||
// 2^256 * 10^18 -1
|
||||
tmp := new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil)
|
||||
tmp = new(big.Int).Sub(new(big.Int).Mul(tmp, precisionReuse), big.NewInt(1))
|
||||
upperLimit = LegacyNewDecFromBigIntWithPrec(tmp, LegacyPrecision)
|
||||
lowerLimit = upperLimit.Neg()
|
||||
}
|
||||
|
||||
func precisionInt() *big.Int {
|
||||
return new(big.Int).Set(precisionReuse)
|
||||
}
|
||||
|
||||
func LegacyZeroDec() LegacyDec { return LegacyDec{new(big.Int).Set(zeroInt)} }
|
||||
func LegacyOneDec() LegacyDec { return LegacyDec{precisionInt()} }
|
||||
func LegacySmallestDec() LegacyDec { return LegacyDec{new(big.Int).Set(oneInt)} }
|
||||
|
||||
// calculate the precision multiplier
|
||||
func calcPrecisionMultiplier(prec int64) *big.Int {
|
||||
if prec < 0 {
|
||||
panic(fmt.Sprintf("negative precision %v", prec))
|
||||
}
|
||||
|
||||
if prec > LegacyPrecision {
|
||||
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", LegacyPrecision, prec))
|
||||
}
|
||||
zerosToAdd := LegacyPrecision - prec
|
||||
multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil)
|
||||
return multiplier
|
||||
}
|
||||
|
||||
// get the precision multiplier, do not mutate result
|
||||
func precisionMultiplier(prec int64) *big.Int {
|
||||
if prec < 0 {
|
||||
panic(fmt.Sprintf("negative precision %v", prec))
|
||||
}
|
||||
|
||||
if prec > LegacyPrecision {
|
||||
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", LegacyPrecision, prec))
|
||||
}
|
||||
return precisionMultipliers[prec]
|
||||
}
|
||||
|
||||
// LegacyNewDec create a new Dec from integer assuming whole number
|
||||
func LegacyNewDec(i int64) LegacyDec {
|
||||
return LegacyNewDecWithPrec(i, 0)
|
||||
}
|
||||
|
||||
// LegacyNewDecWithPrec create a new Dec from integer with decimal place at prec
|
||||
// CONTRACT: prec <= Precision
|
||||
func LegacyNewDecWithPrec(i, prec int64) LegacyDec {
|
||||
bi := big.NewInt(i)
|
||||
return LegacyDec{
|
||||
bi.Mul(bi, precisionMultiplier(prec)),
|
||||
}
|
||||
}
|
||||
|
||||
// LegacyNewDecFromBigInt create a new Dec from big integer assuming whole numbers
|
||||
// CONTRACT: prec <= Precision
|
||||
func LegacyNewDecFromBigInt(i *big.Int) LegacyDec {
|
||||
return LegacyNewDecFromBigIntWithPrec(i, 0)
|
||||
}
|
||||
|
||||
// LegacyNewDecFromBigIntWithPrec create a new Dec from big integer assuming whole numbers
|
||||
// CONTRACT: prec <= Precision
|
||||
func LegacyNewDecFromBigIntWithPrec(i *big.Int, prec int64) LegacyDec {
|
||||
return LegacyDec{
|
||||
new(big.Int).Mul(i, precisionMultiplier(prec)),
|
||||
}
|
||||
}
|
||||
|
||||
// LegacyNewDecFromInt create a new Dec from big integer assuming whole numbers
|
||||
// CONTRACT: prec <= Precision
|
||||
func LegacyNewDecFromInt(i Int) LegacyDec {
|
||||
return LegacyNewDecFromIntWithPrec(i, 0)
|
||||
}
|
||||
|
||||
// LegacyNewDecFromIntWithPrec create a new Dec from big integer with decimal place at prec
|
||||
// CONTRACT: prec <= Precision
|
||||
func LegacyNewDecFromIntWithPrec(i Int, prec int64) LegacyDec {
|
||||
return LegacyDec{
|
||||
new(big.Int).Mul(i.BigIntMut(), precisionMultiplier(prec)),
|
||||
}
|
||||
}
|
||||
|
||||
// LegacyNewDecFromStr create a decimal from an input decimal string.
|
||||
// valid must come in the form:
|
||||
//
|
||||
// (-) whole integers (.) decimal integers
|
||||
//
|
||||
// examples of acceptable input include:
|
||||
//
|
||||
// -123.456
|
||||
// 456.7890
|
||||
// 345
|
||||
// -456789
|
||||
//
|
||||
// NOTE - An error will return if more decimal places
|
||||
// are provided in the string than the constant Precision.
|
||||
//
|
||||
// CONTRACT - This function does not mutate the input str.
|
||||
func LegacyNewDecFromStr(str string) (LegacyDec, error) {
|
||||
// first extract any negative symbol
|
||||
neg := false
|
||||
if len(str) > 0 && str[0] == '-' {
|
||||
neg = true
|
||||
str = str[1:]
|
||||
}
|
||||
|
||||
if len(str) == 0 {
|
||||
return LegacyDec{}, ErrLegacyEmptyDecimalStr
|
||||
}
|
||||
|
||||
strs := strings.Split(str, ".")
|
||||
lenDecs := 0
|
||||
combinedStr := strs[0]
|
||||
|
||||
if len(strs) == 2 { // has a decimal place
|
||||
lenDecs = len(strs[1])
|
||||
if lenDecs == 0 || len(combinedStr) == 0 {
|
||||
return LegacyDec{}, ErrLegacyInvalidDecimalLength
|
||||
}
|
||||
combinedStr += strs[1]
|
||||
} else if len(strs) > 2 {
|
||||
return LegacyDec{}, ErrLegacyInvalidDecimalStr
|
||||
}
|
||||
|
||||
if lenDecs > LegacyPrecision {
|
||||
return LegacyDec{}, fmt.Errorf("value '%s' exceeds max precision by %d decimal places: max precision %d", str, LegacyPrecision-lenDecs, LegacyPrecision)
|
||||
}
|
||||
|
||||
// add some extra zero's to correct to the Precision factor
|
||||
zerosToAdd := LegacyPrecision - lenDecs
|
||||
zeros := strings.Repeat("0", zerosToAdd)
|
||||
combinedStr += zeros
|
||||
|
||||
combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10
|
||||
if !ok {
|
||||
return LegacyDec{}, fmt.Errorf("failed to set decimal string with base 10: %s", combinedStr)
|
||||
}
|
||||
if neg {
|
||||
combined = new(big.Int).Neg(combined)
|
||||
}
|
||||
|
||||
result := LegacyDec{i: combined}
|
||||
if !result.IsInValidRange() {
|
||||
return LegacyDec{}, fmt.Errorf("out of range: %w", ErrLegacyInvalidDecimalStr)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LegacyMustNewDecFromStr Decimal from string, panic on error
|
||||
func LegacyMustNewDecFromStr(s string) LegacyDec {
|
||||
dec, err := LegacyNewDecFromStr(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
func (d LegacyDec) IsNil() bool { return d.i == nil } // is decimal nil
|
||||
func (d LegacyDec) IsZero() bool { return (d.i).Sign() == 0 } // is equal to zero
|
||||
func (d LegacyDec) IsNegative() bool { return (d.i).Sign() == -1 } // is negative
|
||||
func (d LegacyDec) IsPositive() bool { return (d.i).Sign() == 1 } // is positive
|
||||
func (d LegacyDec) Equal(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) == 0 } // equal decimals
|
||||
func (d LegacyDec) GT(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) > 0 } // greater than
|
||||
func (d LegacyDec) GTE(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) >= 0 } // greater than or equal
|
||||
func (d LegacyDec) LT(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) < 0 } // less than
|
||||
func (d LegacyDec) LTE(d2 LegacyDec) bool { return (d.i).Cmp(d2.i) <= 0 } // less than or equal
|
||||
func (d LegacyDec) Neg() LegacyDec { return LegacyDec{new(big.Int).Neg(d.i)} } // reverse the decimal sign
|
||||
func (d LegacyDec) NegMut() LegacyDec { d.i.Neg(d.i); return d } // reverse the decimal sign, mutable
|
||||
func (d LegacyDec) Abs() LegacyDec { return LegacyDec{new(big.Int).Abs(d.i)} } // absolute value
|
||||
func (d LegacyDec) AbsMut() LegacyDec { d.i.Abs(d.i); return d } // absolute value, mutable
|
||||
func (d LegacyDec) Set(d2 LegacyDec) LegacyDec { d.i.Set(d2.i); return d } // set to existing dec value
|
||||
func (d LegacyDec) Clone() LegacyDec { return LegacyDec{new(big.Int).Set(d.i)} } // clone new dec
|
||||
|
||||
// BigInt returns a copy of the underlying big.Int.
|
||||
func (d LegacyDec) BigInt() *big.Int {
|
||||
if d.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
cp := new(big.Int)
|
||||
return cp.Set(d.i)
|
||||
}
|
||||
|
||||
// BigIntMut converts LegacyDec to big.Int, mutative the input
|
||||
func (d LegacyDec) BigIntMut() *big.Int {
|
||||
if d.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d.i
|
||||
}
|
||||
|
||||
func (d LegacyDec) ImmutOp(op func(LegacyDec, LegacyDec) LegacyDec, d2 LegacyDec) LegacyDec {
|
||||
return op(d.Clone(), d2)
|
||||
}
|
||||
|
||||
func (d LegacyDec) ImmutOpInt(op func(LegacyDec, Int) LegacyDec, d2 Int) LegacyDec {
|
||||
return op(d.Clone(), d2)
|
||||
}
|
||||
|
||||
func (d LegacyDec) ImmutOpInt64(op func(LegacyDec, int64) LegacyDec, d2 int64) LegacyDec {
|
||||
// TODO: use already allocated operand bigint to avoid
|
||||
// newint each time, add mutex for race condition
|
||||
// Issue: https://github.com/cosmos/cosmos-sdk/issues/11166
|
||||
return op(d.Clone(), d2)
|
||||
}
|
||||
|
||||
func (d LegacyDec) SetInt64(i int64) LegacyDec {
|
||||
d.i.SetInt64(i)
|
||||
d.i.Mul(d.i, precisionReuse)
|
||||
return d
|
||||
}
|
||||
|
||||
// Add addition
|
||||
func (d LegacyDec) Add(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.AddMut, d2)
|
||||
}
|
||||
|
||||
// AddMut mutable addition
|
||||
func (d LegacyDec) AddMut(d2 LegacyDec) LegacyDec {
|
||||
d.i.Add(d.i, d2.i)
|
||||
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// Sub subtraction
|
||||
func (d LegacyDec) Sub(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.SubMut, d2)
|
||||
}
|
||||
|
||||
// SubMut mutable subtraction
|
||||
func (d LegacyDec) SubMut(d2 LegacyDec) LegacyDec {
|
||||
d.i.Sub(d.i, d2.i)
|
||||
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d LegacyDec) assertInValidRange() {
|
||||
if !d.IsInValidRange() {
|
||||
panic("Int overflow")
|
||||
}
|
||||
}
|
||||
|
||||
// IsInValidRange returns true when the value is between the upper limit of (2^256 * 10^18)
|
||||
// and the lower limit of -1*(2^256 * 10^18).
|
||||
func (d LegacyDec) IsInValidRange() bool {
|
||||
return !(d.GT(upperLimit) || d.LT(lowerLimit))
|
||||
}
|
||||
|
||||
// Mul multiplication
|
||||
func (d LegacyDec) Mul(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.MulMut, d2)
|
||||
}
|
||||
|
||||
// MulMut mutable multiplication
|
||||
func (d LegacyDec) MulMut(d2 LegacyDec) LegacyDec {
|
||||
d.i.Mul(d.i, d2.i)
|
||||
chopped := chopPrecisionAndRound(d.i)
|
||||
|
||||
*d.i = *chopped
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// MulTruncate multiplication truncate
|
||||
func (d LegacyDec) MulTruncate(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.MulTruncateMut, d2)
|
||||
}
|
||||
|
||||
// MulTruncateMut mutable multiplication truncate
|
||||
func (d LegacyDec) MulTruncateMut(d2 LegacyDec) LegacyDec {
|
||||
d.i.Mul(d.i, d2.i)
|
||||
chopPrecisionAndTruncate(d.i)
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// MulRoundUp multiplication round up at precision end.
|
||||
func (d LegacyDec) MulRoundUp(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.MulRoundUpMut, d2)
|
||||
}
|
||||
|
||||
// MulRoundUpMut mutable multiplication with round up at precision end.
|
||||
func (d LegacyDec) MulRoundUpMut(d2 LegacyDec) LegacyDec {
|
||||
d.i.Mul(d.i, d2.i)
|
||||
chopPrecisionAndRoundUp(d.i)
|
||||
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// MulInt multiplication
|
||||
func (d LegacyDec) MulInt(i Int) LegacyDec {
|
||||
return d.ImmutOpInt(LegacyDec.MulIntMut, i)
|
||||
}
|
||||
|
||||
func (d LegacyDec) MulIntMut(i Int) LegacyDec {
|
||||
d.i.Mul(d.i, i.BigIntMut())
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// MulInt64 multiplication with int64
|
||||
func (d LegacyDec) MulInt64(i int64) LegacyDec {
|
||||
return d.ImmutOpInt64(LegacyDec.MulInt64Mut, i)
|
||||
}
|
||||
|
||||
func (d LegacyDec) MulInt64Mut(i int64) LegacyDec {
|
||||
d.i.Mul(d.i, big.NewInt(i))
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// Quo quotient
|
||||
func (d LegacyDec) Quo(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.QuoMut, d2)
|
||||
}
|
||||
|
||||
var squaredPrecisionReuse = new(big.Int).Mul(precisionReuse, precisionReuse)
|
||||
|
||||
// QuoMut mutable quotient
|
||||
func (d LegacyDec) QuoMut(d2 LegacyDec) LegacyDec {
|
||||
// multiply by precision twice
|
||||
d.i.Mul(d.i, squaredPrecisionReuse)
|
||||
d.i.Quo(d.i, d2.i)
|
||||
|
||||
chopPrecisionAndRound(d.i)
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// QuoTruncate quotient truncate
|
||||
func (d LegacyDec) QuoTruncate(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.QuoTruncateMut, d2)
|
||||
}
|
||||
|
||||
// QuoTruncateMut divides the current LegacyDec value by the provided LegacyDec value, truncating the result.
|
||||
func (d LegacyDec) QuoTruncateMut(d2 LegacyDec) LegacyDec {
|
||||
// multiply precision once before performing division
|
||||
d.i.Mul(d.i, precisionReuse)
|
||||
d.i.Quo(d.i, d2.i)
|
||||
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// QuoRoundUp quotient, round up
|
||||
func (d LegacyDec) QuoRoundUp(d2 LegacyDec) LegacyDec {
|
||||
return d.ImmutOp(LegacyDec.QuoRoundupMut, d2)
|
||||
}
|
||||
|
||||
// QuoRoundupMut mutable quotient, round up
|
||||
func (d LegacyDec) QuoRoundupMut(d2 LegacyDec) LegacyDec {
|
||||
// multiply precision twice
|
||||
d.i.Mul(d.i, precisionReuse)
|
||||
_, rem := d.i.QuoRem(d.i, d2.i, big.NewInt(0))
|
||||
if rem.Sign() > 0 && d.IsNegative() == d2.IsNegative() ||
|
||||
rem.Sign() < 0 && d.IsNegative() != d2.IsNegative() {
|
||||
d.i.Add(d.i, oneInt)
|
||||
}
|
||||
d.assertInValidRange()
|
||||
return d
|
||||
}
|
||||
|
||||
// QuoInt quotient
|
||||
func (d LegacyDec) QuoInt(i Int) LegacyDec {
|
||||
return d.ImmutOpInt(LegacyDec.QuoIntMut, i)
|
||||
}
|
||||
|
||||
func (d LegacyDec) QuoIntMut(i Int) LegacyDec {
|
||||
d.i.Quo(d.i, i.BigIntMut())
|
||||
return d
|
||||
}
|
||||
|
||||
// QuoInt64 quotient with int64
|
||||
func (d LegacyDec) QuoInt64(i int64) LegacyDec {
|
||||
return d.ImmutOpInt64(LegacyDec.QuoInt64Mut, i)
|
||||
}
|
||||
|
||||
func (d LegacyDec) QuoInt64Mut(i int64) LegacyDec {
|
||||
d.i.Quo(d.i, big.NewInt(i))
|
||||
return d
|
||||
}
|
||||
|
||||
// ApproxRoot returns an approximate estimation of a Dec's positive real nth root
|
||||
// using Newton's method (where n is positive). The algorithm starts with some guess and
|
||||
// computes the sequence of improved guesses until an answer converges to an
|
||||
// approximate answer. It returns `|d|.ApproxRoot() * -1` if input is negative.
|
||||
// A maximum number of 100 iterations is used a backup boundary condition for
|
||||
// cases where the answer never converges enough to satisfy the main condition.
|
||||
func (d LegacyDec) ApproxRoot(root uint64) (guess LegacyDec, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var ok bool
|
||||
err, ok = r.(error)
|
||||
if !ok {
|
||||
err = errors.New("out of bounds")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if root == 0 {
|
||||
// Return 1 as root 0 of any number is considered 1.
|
||||
return LegacyOneDec(), nil
|
||||
}
|
||||
|
||||
if d.IsNegative() {
|
||||
absRoot, err := d.Neg().ApproxRoot(root)
|
||||
return absRoot.NegMut(), err
|
||||
}
|
||||
|
||||
// Direct return for base cases: d^1 = d or when d is 0 or 1.
|
||||
scratchOneDec := LegacyOneDec()
|
||||
if root == 1 || d.IsZero() || d.Equal(scratchOneDec) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
guess, delta := scratchOneDec, LegacyOneDec()
|
||||
|
||||
for iter := 0; iter < maxApproxRootIterations; iter++ {
|
||||
prev := guess.Power(root - 1)
|
||||
if prev.IsZero() {
|
||||
prev = smallestDec
|
||||
}
|
||||
|
||||
// Compute delta = (d/prev - guess) / root
|
||||
delta.Set(d).QuoMut(prev)
|
||||
delta.SubMut(guess)
|
||||
delta.QuoInt64Mut(int64(root))
|
||||
|
||||
guess.AddMut(delta)
|
||||
|
||||
// Stop when delta is small enough
|
||||
if delta.Abs().LTE(smallestDec) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return guess, nil
|
||||
}
|
||||
|
||||
// Power returns the result of raising to a positive integer power
|
||||
func (d LegacyDec) Power(power uint64) LegacyDec {
|
||||
res := LegacyDec{new(big.Int).Set(d.i)}
|
||||
return res.PowerMut(power)
|
||||
}
|
||||
|
||||
func (d LegacyDec) PowerMut(power uint64) LegacyDec {
|
||||
if power == 0 {
|
||||
// Set to 1 with the correct precision.
|
||||
d.i.Set(precisionReuse)
|
||||
return d
|
||||
}
|
||||
tmp := LegacyOneDec()
|
||||
|
||||
for i := power; i > 1; {
|
||||
if i%2 != 0 {
|
||||
tmp.MulMut(d)
|
||||
}
|
||||
i /= 2
|
||||
d.MulMut(d)
|
||||
}
|
||||
|
||||
return d.MulMut(tmp)
|
||||
}
|
||||
|
||||
// ApproxSqrt is a wrapper around ApproxRoot for the common special case
|
||||
// of finding the square root of a number. It returns -(sqrt(abs(d)) if input is negative.
|
||||
func (d LegacyDec) ApproxSqrt() (LegacyDec, error) {
|
||||
return d.ApproxRoot(2)
|
||||
}
|
||||
|
||||
// IsInteger is integer, e.g. decimals are zero
|
||||
func (d LegacyDec) IsInteger() bool {
|
||||
return new(big.Int).Rem(d.i, precisionReuse).Sign() == 0
|
||||
}
|
||||
|
||||
// Format format decimal state
|
||||
func (d LegacyDec) Format(s fmt.State, verb rune) {
|
||||
_, err := s.Write([]byte(d.String()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d LegacyDec) String() string {
|
||||
if d.i == nil {
|
||||
return d.i.String()
|
||||
}
|
||||
|
||||
isNeg := d.IsNegative()
|
||||
|
||||
if isNeg {
|
||||
d = d.Neg()
|
||||
}
|
||||
|
||||
bzInt, err := d.i.MarshalText()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
inputSize := len(bzInt)
|
||||
|
||||
var bzStr []byte
|
||||
|
||||
// TODO: Remove trailing zeros
|
||||
// case 1, purely decimal
|
||||
if inputSize <= LegacyPrecision {
|
||||
bzStr = make([]byte, LegacyPrecision+2)
|
||||
|
||||
// 0. prefix
|
||||
bzStr[0] = byte('0')
|
||||
bzStr[1] = byte('.')
|
||||
|
||||
// set relevant digits to 0
|
||||
for i := 0; i < LegacyPrecision-inputSize; i++ {
|
||||
bzStr[i+2] = byte('0')
|
||||
}
|
||||
|
||||
// set final digits
|
||||
copy(bzStr[2+(LegacyPrecision-inputSize):], bzInt)
|
||||
} else {
|
||||
// inputSize + 1 to account for the decimal point that is being added
|
||||
bzStr = make([]byte, inputSize+1)
|
||||
decPointPlace := inputSize - LegacyPrecision
|
||||
|
||||
copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits
|
||||
bzStr[decPointPlace] = byte('.') // decimal point
|
||||
copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits
|
||||
}
|
||||
|
||||
if isNeg {
|
||||
return "-" + string(bzStr)
|
||||
}
|
||||
|
||||
return string(bzStr)
|
||||
}
|
||||
|
||||
// Float64 returns the float64 representation of a Dec.
|
||||
// Will return the error if the conversion failed.
|
||||
func (d LegacyDec) Float64() (float64, error) {
|
||||
return strconv.ParseFloat(d.String(), 64)
|
||||
}
|
||||
|
||||
// MustFloat64 returns the float64 representation of a Dec.
|
||||
// Would panic if the conversion failed.
|
||||
func (d LegacyDec) MustFloat64() float64 {
|
||||
if value, err := strconv.ParseFloat(d.String(), 64); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// ____
|
||||
// __| |__ "chop 'em
|
||||
// ` \ round!"
|
||||
// ___|| ~ _ -bankers
|
||||
// | | __
|
||||
// | | | __|__|__
|
||||
// |_____: / | $$$ |
|
||||
// |________|
|
||||
|
||||
// Remove a Precision amount of rightmost digits and perform bankers rounding
|
||||
// on the remainder (gaussian rounding) on the digits which have been removed.
|
||||
//
|
||||
// Mutates the input. Use the non-mutative version if that is undesired
|
||||
func chopPrecisionAndRound(d *big.Int) *big.Int {
|
||||
// remove the negative and add it back when returning
|
||||
if d.Sign() == -1 {
|
||||
// make d positive, compute chopped value, and then un-mutate d
|
||||
d = d.Neg(d)
|
||||
d = chopPrecisionAndRound(d)
|
||||
d = d.Neg(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// get the truncated quotient and remainder
|
||||
quo, rem := d, big.NewInt(0)
|
||||
quo, rem = quo.QuoRem(d, precisionReuse, rem)
|
||||
|
||||
if rem.Sign() == 0 { // remainder is zero
|
||||
return quo
|
||||
}
|
||||
|
||||
switch rem.Cmp(fivePrecision) {
|
||||
case -1:
|
||||
return quo
|
||||
case 1:
|
||||
return quo.Add(quo, oneInt)
|
||||
default: // bankers rounding must take place
|
||||
// always round to an even number
|
||||
if quo.Bit(0) == 0 {
|
||||
return quo
|
||||
}
|
||||
return quo.Add(quo, oneInt)
|
||||
}
|
||||
}
|
||||
|
||||
func chopPrecisionAndRoundUp(d *big.Int) *big.Int {
|
||||
// remove the negative and add it back when returning
|
||||
if d.Sign() == -1 {
|
||||
// make d positive, compute chopped value, and then un-mutate d
|
||||
d = d.Neg(d)
|
||||
// truncate since d is negative...
|
||||
chopPrecisionAndTruncate(d)
|
||||
d = d.Neg(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// get the truncated quotient and remainder
|
||||
quo, rem := d, big.NewInt(0)
|
||||
quo, rem = quo.QuoRem(d, precisionReuse, rem)
|
||||
|
||||
if rem.Sign() == 0 { // remainder is zero
|
||||
return quo
|
||||
}
|
||||
|
||||
return quo.Add(quo, oneInt)
|
||||
}
|
||||
|
||||
func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int {
|
||||
tmp := new(big.Int).Set(d)
|
||||
return chopPrecisionAndRound(tmp)
|
||||
}
|
||||
|
||||
// RoundInt64 rounds the decimal using bankers rounding
|
||||
func (d LegacyDec) RoundInt64() int64 {
|
||||
chopped := chopPrecisionAndRoundNonMutative(d.i)
|
||||
if !chopped.IsInt64() {
|
||||
panic("Int64() out of bound")
|
||||
}
|
||||
return chopped.Int64()
|
||||
}
|
||||
|
||||
// RoundInt round the decimal using bankers rounding
|
||||
func (d LegacyDec) RoundInt() Int {
|
||||
return NewIntFromBigIntMut(chopPrecisionAndRoundNonMutative(d.i))
|
||||
}
|
||||
|
||||
// chopPrecisionAndTruncate is similar to chopPrecisionAndRound,
|
||||
// but always rounds down. It does not mutate the input.
|
||||
func chopPrecisionAndTruncate(d *big.Int) {
|
||||
d.Quo(d, precisionReuse)
|
||||
}
|
||||
|
||||
func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int {
|
||||
tmp := new(big.Int).Set(d)
|
||||
chopPrecisionAndTruncate(tmp)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// TruncateInt64 truncates the decimals from the number and returns an int64
|
||||
func (d LegacyDec) TruncateInt64() int64 {
|
||||
chopped := chopPrecisionAndTruncateNonMutative(d.i)
|
||||
if !chopped.IsInt64() {
|
||||
panic("Int64() out of bound")
|
||||
}
|
||||
return chopped.Int64()
|
||||
}
|
||||
|
||||
// TruncateInt truncates the decimals from the number and returns an Int
|
||||
func (d LegacyDec) TruncateInt() Int {
|
||||
return NewIntFromBigIntMut(chopPrecisionAndTruncateNonMutative(d.i))
|
||||
}
|
||||
|
||||
// TruncateDec truncates the decimals from the number and returns a Dec
|
||||
func (d LegacyDec) TruncateDec() LegacyDec {
|
||||
return LegacyNewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.i))
|
||||
}
|
||||
|
||||
// Ceil returns the smallest integer value (as a decimal) that is greater than
|
||||
// or equal to the given decimal.
|
||||
func (d LegacyDec) Ceil() LegacyDec {
|
||||
tmp := new(big.Int).Set(d.i)
|
||||
|
||||
quo, rem := tmp, big.NewInt(0)
|
||||
quo, rem = quo.QuoRem(tmp, precisionReuse, rem)
|
||||
|
||||
// no need to round with a zero remainder regardless of sign
|
||||
var r LegacyDec
|
||||
switch rem.Sign() {
|
||||
case 0:
|
||||
r = LegacyNewDecFromBigInt(quo)
|
||||
case -1:
|
||||
r = LegacyNewDecFromBigInt(quo)
|
||||
default:
|
||||
r = LegacyNewDecFromBigInt(quo.Add(quo, oneInt))
|
||||
}
|
||||
r.assertInValidRange()
|
||||
return r
|
||||
}
|
||||
|
||||
// LegacyMaxSortableDec is the largest Dec that can be passed into SortableDecBytes()
|
||||
// Its negative form is the least Dec that can be passed in.
|
||||
var LegacyMaxSortableDec LegacyDec
|
||||
|
||||
func init() {
|
||||
LegacyMaxSortableDec = LegacyOneDec().Quo(LegacySmallestDec())
|
||||
}
|
||||
|
||||
// LegacyValidSortableDec ensures that a Dec is within the sortable bounds,
|
||||
// a Dec can't have a precision of less than 10^-18.
|
||||
// Max sortable decimal was set to the reciprocal of SmallestDec.
|
||||
func LegacyValidSortableDec(dec LegacyDec) bool {
|
||||
return dec.Abs().LTE(LegacyMaxSortableDec)
|
||||
}
|
||||
|
||||
// LegacySortableDecBytes returns a byte slice representation of a Dec that can be sorted.
|
||||
// Left and right pads with 0s so there are 18 digits to left and right of the decimal point.
|
||||
// For this reason, there is a maximum and minimum value for this, enforced by ValidSortableDec.
|
||||
func LegacySortableDecBytes(dec LegacyDec) []byte {
|
||||
if !LegacyValidSortableDec(dec) {
|
||||
panic("dec must be within bounds")
|
||||
}
|
||||
// Instead of adding an extra byte to all sortable decs in order to handle max sortable, we just
|
||||
// makes its bytes be "max" which comes after all numbers in ASCIIbetical order
|
||||
if dec.Equal(LegacyMaxSortableDec) {
|
||||
return []byte("max")
|
||||
}
|
||||
// For the same reason, we make the bytes of minimum sortable dec be --, which comes before all numbers.
|
||||
if dec.Equal(LegacyMaxSortableDec.Neg()) {
|
||||
return []byte("--")
|
||||
}
|
||||
// We move the negative sign to the front of all the left padded 0s, to make negative numbers come before positive numbers
|
||||
if dec.IsNegative() {
|
||||
return append([]byte("-"), []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", LegacyPrecision*2+1), dec.Abs().String()))...)
|
||||
}
|
||||
return []byte(fmt.Sprintf(fmt.Sprintf("%%0%ds", LegacyPrecision*2+1), dec.String()))
|
||||
}
|
||||
|
||||
// reuse nil values
|
||||
var nilJSON []byte
|
||||
|
||||
func init() {
|
||||
empty := new(big.Int)
|
||||
bz, _ := empty.MarshalText()
|
||||
nilJSON, _ = json.Marshal(string(bz))
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the decimal
|
||||
func (d LegacyDec) MarshalJSON() ([]byte, error) {
|
||||
if d.i == nil {
|
||||
return nilJSON, nil
|
||||
}
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON defines custom decoding scheme
|
||||
func (d *LegacyDec) UnmarshalJSON(bz []byte) error {
|
||||
if d.i == nil {
|
||||
d.i = new(big.Int)
|
||||
}
|
||||
|
||||
var text string
|
||||
err := json.Unmarshal(bz, &text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Reuse dec allocation
|
||||
newDec, err := LegacyNewDecFromStr(text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.i = newDec.i
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML returns the YAML representation.
|
||||
func (d LegacyDec) MarshalYAML() (interface{}, error) {
|
||||
return d.String(), nil
|
||||
}
|
||||
|
||||
// Marshal implements the gogo proto custom type interface.
|
||||
func (d LegacyDec) Marshal() ([]byte, error) {
|
||||
i := d.i
|
||||
if i == nil {
|
||||
i = new(big.Int)
|
||||
}
|
||||
return i.MarshalText()
|
||||
}
|
||||
|
||||
// MarshalTo implements the gogo proto custom type interface.
|
||||
func (d *LegacyDec) MarshalTo(data []byte) (n int, err error) {
|
||||
i := d.i
|
||||
if i == nil {
|
||||
i = new(big.Int)
|
||||
}
|
||||
|
||||
if i.Sign() == 0 {
|
||||
copy(data, []byte{0x30})
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
bz, err := d.Marshal()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
copy(data, bz)
|
||||
return len(bz), nil
|
||||
}
|
||||
|
||||
// Unmarshal implements the gogo proto custom type interface.
|
||||
func (d *LegacyDec) Unmarshal(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
d = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.i == nil {
|
||||
d.i = new(big.Int)
|
||||
}
|
||||
|
||||
if err := d.i.UnmarshalText(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !d.IsInValidRange() {
|
||||
return errors.New("decimal out of range")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size implements the gogo proto custom type interface.
|
||||
func (d *LegacyDec) Size() int {
|
||||
bz, _ := d.Marshal()
|
||||
return len(bz)
|
||||
}
|
||||
|
||||
// MarshalAmino Override Amino binary serialization by proxying to protobuf.
|
||||
func (d LegacyDec) MarshalAmino() ([]byte, error) { return d.Marshal() }
|
||||
func (d *LegacyDec) UnmarshalAmino(bz []byte) error { return d.Unmarshal(bz) }
|
||||
|
||||
// helpers
|
||||
|
||||
// LegacyDecsEqual return true if two decimal arrays are equal.
|
||||
func LegacyDecsEqual(d1s, d2s []LegacyDec) bool {
|
||||
if len(d1s) != len(d2s) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, d1 := range d1s {
|
||||
if !d1.Equal(d2s[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// LegacyMinDec minimum decimal between two
|
||||
func LegacyMinDec(d1, d2 LegacyDec) LegacyDec {
|
||||
if d1.LT(d2) {
|
||||
return d1
|
||||
}
|
||||
return d2
|
||||
}
|
||||
|
||||
// LegacyMaxDec maximum decimal between two
|
||||
func LegacyMaxDec(d1, d2 LegacyDec) LegacyDec {
|
||||
if d1.LT(d2) {
|
||||
return d2
|
||||
}
|
||||
return d1
|
||||
}
|
||||
|
||||
// LegacyDecEq intended to be used with require/assert: require.True(DecEq(...))
|
||||
func LegacyDecEq(t *testing.T, exp, got LegacyDec) (*testing.T, bool, string, string, string) {
|
||||
t.Helper()
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
|
||||
}
|
||||
|
||||
func LegacyDecApproxEq(t *testing.T, d1, d2, tol LegacyDec) (*testing.T, bool, string, string, string) {
|
||||
t.Helper()
|
||||
diff := d1.Sub(d2).Abs()
|
||||
return t, diff.LTE(tol), "expected |d1 - d2| <:\t%v\ngot |d1 - d2| = \t\t%v", tol.String(), diff.String()
|
||||
}
|
||||
|
||||
// FormatDec formats a decimal (as encoded in protobuf) into a value-rendered
|
||||
// string following ADR-050. This function operates with string manipulation
|
||||
// (instead of manipulating the sdk.Dec object).
|
||||
func FormatDec(v string) (string, error) {
|
||||
parts := strings.Split(v, ".")
|
||||
if len(parts) > 2 {
|
||||
return "", fmt.Errorf("invalid decimal: too many points in %s", v)
|
||||
}
|
||||
|
||||
intPart, err := FormatInt(parts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
return intPart, nil
|
||||
}
|
||||
|
||||
decPart := strings.TrimRight(parts[1], "0")
|
||||
if len(decPart) == 0 {
|
||||
return intPart, nil
|
||||
}
|
||||
|
||||
// Ensure that the decimal part has only digits.
|
||||
// https://github.com/cosmos/cosmos-sdk/issues/12811
|
||||
if !hasOnlyDigits(decPart) {
|
||||
return "", fmt.Errorf("non-digits detected after decimal point in: %q", decPart)
|
||||
}
|
||||
|
||||
return intPart + "." + decPart, nil
|
||||
}
|
||||
@ -22,9 +22,3 @@ func FuzzLegacyNewDecFromStr(f *testing.F) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDecNegativePrecision(t *testing.T) {
|
||||
t.Skip("https://github.com/cosmos/cosmos-sdk/issues/14004 is not yet addressed")
|
||||
|
||||
LegacyNewDecWithPrec(10, -1)
|
||||
}
|
||||
@ -90,7 +90,6 @@ func (s *decimalInternalTestSuite) TestDecMarshalJSON() {
|
||||
{"12340Int", LegacyNewDec(12340), "\"12340.000000000000000000\"", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
s.T().Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.d.MarshalJSON()
|
||||
if (err != nil) != tt.wantErr {
|
||||
1328
math/legacy_dec_test.go
Normal file
1328
math/legacy_dec_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,6 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
)
|
||||
import "cmp"
|
||||
|
||||
func Max[T cmp.Ordered](a, b T, rest ...T) T {
|
||||
max := a
|
||||
|
||||
16
math/sonar-project.properties
Normal file
16
math/sonar-project.properties
Normal file
@ -0,0 +1,16 @@
|
||||
sonar.projectKey=cosmos-sdk-math
|
||||
sonar.organization=cosmos
|
||||
|
||||
sonar.projectName=Cosmos SDK - Math
|
||||
sonar.project.monorepo.enabled=true
|
||||
|
||||
sonar.sources=.
|
||||
sonar.exclusions=**/*_test.go,**/*.pb.go,**/*.pulsar.go,**/*.pb.gw.go
|
||||
sonar.coverage.exclusions=**/*_test.go,**/testutil/**,**/*.pb.go,**/*.pb.gw.go,**/*.pulsar.go,test_helpers.go,docs/**
|
||||
sonar.tests=.
|
||||
sonar.test.inclusions=**/*_test.go
|
||||
sonar.go.coverage.reportPaths=coverage.out
|
||||
|
||||
sonar.sourceEncoding=UTF-8
|
||||
sonar.scm.provider=git
|
||||
sonar.scm.forceReloadAll=true
|
||||
38
math/uint.go
38
math/uint.go
@ -15,24 +15,36 @@ type Uint struct {
|
||||
|
||||
// BigInt converts Uint to big.Int
|
||||
func (u Uint) BigInt() *big.Int {
|
||||
if u.IsNil() {
|
||||
return nil
|
||||
}
|
||||
return new(big.Int).Set(u.i)
|
||||
}
|
||||
|
||||
// BigIntMut converts Uint to big.Int, mutative the input
|
||||
func (u Uint) BigIntMut() *big.Int {
|
||||
if u.IsNil() {
|
||||
return nil
|
||||
}
|
||||
return u.i
|
||||
}
|
||||
|
||||
// IsNil returns true if Uint is uninitialized
|
||||
func (u Uint) IsNil() bool {
|
||||
return u.i == nil
|
||||
}
|
||||
|
||||
// NewUintFromBigUint constructs Uint from big.Uint
|
||||
// NewUintFromBigInt constructs Uint from big.Int
|
||||
// Panics if i is negative or wider than 256 bits
|
||||
func NewUintFromBigInt(i *big.Int) Uint {
|
||||
u, err := checkNewUint(i)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("overflow: %s", err))
|
||||
panic(fmt.Errorf("overflow: %w", err))
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// NewUint constructs Uint from int64
|
||||
// NewUint constructs Uint from uint64
|
||||
func NewUint(n uint64) Uint {
|
||||
i := new(big.Int)
|
||||
i.SetUint64(n)
|
||||
@ -40,6 +52,7 @@ func NewUint(n uint64) Uint {
|
||||
}
|
||||
|
||||
// NewUintFromString constructs Uint from string
|
||||
// Panics if parsed s is negative or wider than 256 bits
|
||||
func NewUintFromString(s string) Uint {
|
||||
u, err := ParseUint(s)
|
||||
if err != nil {
|
||||
@ -86,7 +99,7 @@ func (u Uint) LTE(u2 Uint) bool { return !u.GT(u2) }
|
||||
// Add adds Uint from another
|
||||
func (u Uint) Add(u2 Uint) Uint { return NewUintFromBigInt(new(big.Int).Add(u.i, u2.i)) }
|
||||
|
||||
// Add convert uint64 and add it to Uint
|
||||
// AddUint64 convert uint64 and add it to Uint
|
||||
func (u Uint) AddUint64(u2 uint64) Uint { return u.Add(NewUint(u2)) }
|
||||
|
||||
// Sub adds Uint from another
|
||||
@ -100,13 +113,14 @@ func (u Uint) Mul(u2 Uint) (res Uint) {
|
||||
return NewUintFromBigInt(new(big.Int).Mul(u.i, u2.i))
|
||||
}
|
||||
|
||||
// Mul multiplies two Uints
|
||||
// MulUint64 multiplies two Uints
|
||||
func (u Uint) MulUint64(u2 uint64) (res Uint) { return u.Mul(NewUint(u2)) }
|
||||
|
||||
// Quo divides Uint with Uint
|
||||
func (u Uint) Quo(u2 Uint) (res Uint) { return NewUintFromBigInt(div(u.i, u2.i)) }
|
||||
|
||||
// Mod returns remainder after dividing with Uint
|
||||
// Panics if u2 is zero
|
||||
func (u Uint) Mod(u2 Uint) Uint {
|
||||
if u2.IsZero() {
|
||||
panic("division-by-zero")
|
||||
@ -125,16 +139,16 @@ func (u Uint) Decr() Uint {
|
||||
return u.Sub(OneUint())
|
||||
}
|
||||
|
||||
// Quo divides Uint with uint64
|
||||
// QuoUint64 divides Uint with uint64
|
||||
func (u Uint) QuoUint64(u2 uint64) Uint { return u.Quo(NewUint(u2)) }
|
||||
|
||||
// Return the minimum of the Uints
|
||||
// MinUint returns the minimum of the Uints
|
||||
func MinUint(u1, u2 Uint) Uint { return NewUintFromBigInt(min(u1.i, u2.i)) }
|
||||
|
||||
// Return the maximum of the Uints
|
||||
// MaxUint returns the maximum of the Uints
|
||||
func MaxUint(u1, u2 Uint) Uint { return NewUintFromBigInt(max(u1.i, u2.i)) }
|
||||
|
||||
// Human readable string
|
||||
// String returns human-readable string
|
||||
func (u Uint) String() string { return u.i.String() }
|
||||
|
||||
// MarshalJSON defines custom encoding scheme
|
||||
@ -205,7 +219,7 @@ func (u *Uint) Size() int {
|
||||
return len(bz)
|
||||
}
|
||||
|
||||
// Override Amino binary serialization by proxying to protobuf.
|
||||
// MarshalAmino override Amino binary serialization by proxying to protobuf.
|
||||
func (u Uint) MarshalAmino() ([]byte, error) { return u.Marshal() }
|
||||
func (u *Uint) UnmarshalAmino(bz []byte) error { return u.Unmarshal(bz) }
|
||||
|
||||
@ -235,7 +249,7 @@ func checkNewUint(i *big.Int) (Uint, error) {
|
||||
if err := UintOverflow(i); err != nil {
|
||||
return Uint{}, err
|
||||
}
|
||||
return Uint{i}, nil
|
||||
return Uint{new(big.Int).Set(i)}, nil
|
||||
}
|
||||
|
||||
// RelativePow raises x to the power of n, where x (and the result, z) are scaled by factor b
|
||||
@ -247,7 +261,7 @@ func RelativePow(x, n, b Uint) (z Uint) {
|
||||
return z
|
||||
}
|
||||
z = ZeroUint() // otherwise 0^a = 0
|
||||
return
|
||||
return z
|
||||
}
|
||||
|
||||
z = x
|
||||
|
||||
@ -69,6 +69,8 @@ func (s *uintTestSuite) TestUintPanics() {
|
||||
s.Require().Panics(func() { uintmin.Sub(sdkmath.OneUint()) })
|
||||
s.Require().Panics(func() { uintmin.Decr() })
|
||||
|
||||
s.Require().NotPanics(func() { sdkmath.Uint{}.BigInt() })
|
||||
|
||||
s.Require().Equal(uint64(0), sdkmath.MinUint(sdkmath.ZeroUint(), sdkmath.OneUint()).Uint64())
|
||||
s.Require().Equal(uint64(1), sdkmath.MaxUint(sdkmath.ZeroUint(), sdkmath.OneUint()).Uint64())
|
||||
|
||||
@ -97,6 +99,26 @@ func (s *uintTestSuite) TestIsNil() {
|
||||
s.Require().True(sdkmath.Uint{}.IsNil())
|
||||
}
|
||||
|
||||
func (s *uintTestSuite) TestConvertToBigIntMutativeForUint() {
|
||||
r := big.NewInt(30)
|
||||
i := sdkmath.NewUintFromBigInt(r)
|
||||
|
||||
// Compare value of BigInt & BigIntMut
|
||||
s.Require().Equal(i.BigInt(), i.BigIntMut())
|
||||
|
||||
// Modify BigIntMut() pointer and ensure i.BigIntMut() & i.BigInt() change
|
||||
p1 := i.BigIntMut()
|
||||
p1.SetInt64(40)
|
||||
s.Require().Equal(big.NewInt(40), i.BigIntMut())
|
||||
s.Require().Equal(big.NewInt(40), i.BigInt())
|
||||
|
||||
// Modify big.Int() pointer and ensure i.BigIntMut() & i.BigInt() don't change
|
||||
p2 := i.BigInt()
|
||||
p2.SetInt64(50)
|
||||
s.Require().NotEqual(big.NewInt(50), i.BigIntMut())
|
||||
s.Require().NotEqual(big.NewInt(50), i.BigInt())
|
||||
}
|
||||
|
||||
func (s *uintTestSuite) TestArithUint() {
|
||||
for d := 0; d < 1000; d++ {
|
||||
n1 := uint64(rand.Uint32())
|
||||
@ -222,7 +244,7 @@ func (s *uintTestSuite) TestSafeSub() {
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
if tc.panic {
|
||||
s.Require().Panics(func() { tc.x.Sub(tc.y) })
|
||||
continue
|
||||
@ -261,6 +283,16 @@ func (s *uintTestSuite) TestParseUint() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *uintTestSuite) TestNewUintFromBigInt() {
|
||||
r := big.NewInt(42)
|
||||
i := sdkmath.NewUintFromBigInt(r)
|
||||
s.Require().Equal(r, i.BigInt())
|
||||
|
||||
// modify r and ensure i doesn't change
|
||||
r = r.SetInt64(100)
|
||||
s.Require().NotEqual(r, i.BigInt())
|
||||
}
|
||||
|
||||
func randuint() sdkmath.Uint {
|
||||
return sdkmath.NewUint(rand.Uint64())
|
||||
}
|
||||
@ -311,7 +343,6 @@ func TestRoundTripMarshalToUint(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
value := value
|
||||
t.Run(fmt.Sprintf("%d", value), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user