feat: Add decimal support to group module (#10035)
<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->
## Description
Closes: #9895
Add decimal support to group module based on a streamlined version of 3265a868bf/types/math
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
parent
58673f85fa
commit
6e70d25dac
|
@ -13,6 +13,9 @@ const RootCodespace = "sdk"
|
|||
// UndefinedCodespace when we explicitly declare no codespace
|
||||
const UndefinedCodespace = "undefined"
|
||||
|
||||
// mathCodespace is the codespace for all errors defined in math package
|
||||
const mathCodespace = "math"
|
||||
|
||||
var (
|
||||
// errInternal should never be exposed, but we reserve this code for non-specified errors
|
||||
errInternal = Register(UndefinedCodespace, 1, "internal")
|
||||
|
@ -147,6 +150,9 @@ var (
|
|||
|
||||
// ErrAppConfig defines an error occurred if min-gas-prices field in BaseConfig is empty.
|
||||
ErrAppConfig = Register(RootCodespace, 40, "error in app.toml")
|
||||
|
||||
// ErrInvalidDecString defines an error for an invalid decimal string
|
||||
ErrInvalidDecString = Register(mathCodespace, 41, "invalid decimal string")
|
||||
)
|
||||
|
||||
// Register returns an error instance that should be used as the base for
|
||||
|
|
|
@ -3,11 +3,14 @@ go 1.17
|
|||
module github.com/cosmos/cosmos-sdk/x/group
|
||||
|
||||
require (
|
||||
github.com/cockroachdb/apd/v2 v2.0.2
|
||||
github.com/cosmos/cosmos-sdk v0.44.0
|
||||
github.com/gogo/protobuf v1.3.3
|
||||
github.com/regen-network/cosmos-proto v0.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
google.golang.org/grpc v1.40.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
pgregory.net/rapid v0.4.7
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -45,6 +48,7 @@ require (
|
|||
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.30.0 // indirect
|
||||
|
@ -63,13 +67,14 @@ require (
|
|||
github.com/tendermint/tendermint v0.34.13 // indirect
|
||||
github.com/tendermint/tm-db v0.6.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace google.golang.org/grpc => google.golang.org/grpc v1.33.2
|
||||
|
|
|
@ -123,6 +123,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
|||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E=
|
||||
github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.6.10 h1:rgHD/nHjxLh0lMEdfGDqpTtlvtSBwULqrrZ2qPdNaCM=
|
||||
|
@ -149,7 +151,7 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY
|
|||
github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd/go.mod h1:3xOIaNNX19p0QrX0VqWa6voPRoJRGGYtny+DH8NEPvE=
|
||||
github.com/cosmos/iavl v0.15.0-rc5/go.mod h1:WqoPL9yPTQ85QBMT45OOUzPxG/U/JcJoN7uMjgxke/I=
|
||||
github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4=
|
||||
github.com/cosmos/iavl v0.16.0 h1:ICIOB8xysirTX27GmVAaoeSpeozzgSu9d49w36xkVJA=
|
||||
github.com/cosmos/iavl v0.17.1 h1:b/Cl8h1PRMvsu24+TYNlKchIu7W6tmxIBGe6E9u2Ybw=
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
|
||||
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
|
@ -403,8 +405,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
|
||||
|
@ -464,13 +467,13 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
|
@ -708,8 +711,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -1101,6 +1104,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
|||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g=
|
||||
pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// Package math provides helper functions for doing mathematical calculations and parsing for the ecocredit module.
|
||||
package math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/apd/v2"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// Dec is a wrapper struct around apd.Decimal that does no mutation of apd.Decimal's when performing
|
||||
// arithmetic, instead creating a new apd.Decimal for every operation ensuring usage is safe.
|
||||
//
|
||||
// Using apd.Decimal directly can be unsafe because apd operations mutate the underlying Decimal,
|
||||
// but when copying the big.Int structure can be shared between Decimal instances causing corruption.
|
||||
// This was originally discovered in regen0-network/mainnet#15.
|
||||
type Dec struct {
|
||||
dec apd.Decimal
|
||||
}
|
||||
|
||||
func NewDecFromString(s string) (Dec, error) {
|
||||
d, _, err := apd.NewFromString(s)
|
||||
if err != nil {
|
||||
return Dec{}, errors.ErrInvalidDecString.Wrap(err.Error())
|
||||
}
|
||||
return Dec{*d}, nil
|
||||
}
|
||||
|
||||
func NewDecFromInt64(x int64) Dec {
|
||||
var res Dec
|
||||
res.dec.SetInt64(x)
|
||||
return res
|
||||
}
|
||||
|
||||
// Add returns a new Dec with value `x+y` without mutating any argument and error if
|
||||
// there is an overflow.
|
||||
func (x Dec) Add(y Dec) (Dec, error) {
|
||||
var z Dec
|
||||
_, err := apd.BaseContext.Add(&z.dec, &x.dec, &y.dec)
|
||||
return z, errors.Wrap(err, "decimal addition error")
|
||||
}
|
||||
|
||||
// Sub returns a new Dec with value `x-y` without mutating any argument and error if
|
||||
// there is an overflow.
|
||||
func (x Dec) Sub(y Dec) (Dec, error) {
|
||||
var z Dec
|
||||
_, err := apd.BaseContext.Sub(&z.dec, &x.dec, &y.dec)
|
||||
return z, errors.Wrap(err, "decimal subtraction error")
|
||||
}
|
||||
|
||||
func (x Dec) Int64() (int64, error) {
|
||||
return x.dec.Int64()
|
||||
}
|
||||
|
||||
func (x Dec) Cmp(y Dec) int {
|
||||
return x.dec.Cmp(&y.dec)
|
||||
}
|
||||
|
||||
func (x Dec) IsEqual(y Dec) bool {
|
||||
return x.dec.Cmp(&y.dec) == 0
|
||||
}
|
||||
|
||||
func (x Dec) IsNegative() bool {
|
||||
return x.dec.Negative && !x.dec.IsZero()
|
||||
}
|
||||
|
||||
// Add adds x and y
|
||||
func Add(x Dec, y Dec) (Dec, error) {
|
||||
return x.Add(y)
|
||||
}
|
||||
|
||||
// SubNonNegative subtracts the value of y from x and returns the result with
|
||||
// arbitrary precision. Returns an error if the result is negative.
|
||||
func SubNonNegative(x Dec, y Dec) (Dec, error) {
|
||||
z, err := x.Sub(y)
|
||||
if err != nil {
|
||||
return Dec{}, err
|
||||
}
|
||||
|
||||
if z.IsNegative() {
|
||||
return z, fmt.Errorf("result negative during non-negative subtraction")
|
||||
}
|
||||
|
||||
return z, nil
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package math
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"pgregory.net/rapid"
|
||||
)
|
||||
|
||||
func TestDec(t *testing.T) {
|
||||
// Property tests
|
||||
t.Run("TestNewDecFromInt64", rapid.MakeCheck(testDecInt64))
|
||||
|
||||
// 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 combining operations
|
||||
t.Run("TestSubAdd", rapid.MakeCheck(testSubAdd))
|
||||
t.Run("TestAddSub", rapid.MakeCheck(testAddSub))
|
||||
|
||||
// Properties about comparision and equality
|
||||
t.Run("TestCmpInverse", rapid.MakeCheck(testCmpInverse))
|
||||
t.Run("TestEqualCommutative", rapid.MakeCheck(testEqualCommutative))
|
||||
|
||||
// Properties about tests on a single Dec
|
||||
t.Run("TestIsNegative", rapid.MakeCheck(testIsNegative))
|
||||
|
||||
// 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)
|
||||
|
||||
res, err := two.Add(zero)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsEqual(two))
|
||||
|
||||
res, err = five.Sub(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsEqual(three))
|
||||
|
||||
res, err = onePointOneFive.Add(twoPointThreeFour)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsEqual(threePointFourNine))
|
||||
|
||||
res, err = threePointFourNine.Sub(two)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsEqual(onePointFourNine))
|
||||
|
||||
res, err = minusOne.Sub(four)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.IsEqual(minusFivePointZero))
|
||||
|
||||
require.False(t, zero.IsNegative())
|
||||
require.False(t, one.IsNegative())
|
||||
require.True(t, minusOne.IsNegative())
|
||||
}
|
||||
|
||||
var genDec *rapid.Generator = rapid.Custom(func(t *rapid.T) Dec {
|
||||
f := rapid.Float64().Draw(t, "f").(float64)
|
||||
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 = rapid.Custom(func(t *rapid.T) floatAndDec {
|
||||
f := rapid.Float64().Draw(t, "f").(float64)
|
||||
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").(int64)
|
||||
nOut, err := NewDecFromInt64(nIn).Int64()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, nIn, nOut)
|
||||
}
|
||||
|
||||
// Property: 0 + a == a
|
||||
func testAddLeftIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := zero.Add(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.IsEqual(b))
|
||||
}
|
||||
|
||||
// Property: a + 0 == a
|
||||
func testAddRightIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Add(zero)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.IsEqual(b))
|
||||
}
|
||||
|
||||
// Property: a + b == b + a
|
||||
func testAddCommutative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
|
||||
c, err := a.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := b.Add(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, c.IsEqual(d))
|
||||
}
|
||||
|
||||
// Property: (a + b) + c == a + (b + c)
|
||||
func testAddAssociative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
c := genDec.Draw(t, "c").(Dec)
|
||||
|
||||
// (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.IsEqual(g))
|
||||
}
|
||||
|
||||
// Property: a - 0 == a
|
||||
func testSubRightIdentity(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Sub(zero)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.IsEqual(b))
|
||||
}
|
||||
|
||||
// Property: a - a == 0
|
||||
func testSubZero(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
zero := NewDecFromInt64(0)
|
||||
|
||||
b, err := a.Sub(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, b.IsEqual(zero))
|
||||
}
|
||||
|
||||
// Property: (a - b) + b == a
|
||||
func testSubAdd(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
|
||||
c, err := a.Sub(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.IsEqual(d))
|
||||
}
|
||||
|
||||
// Property: (a + b) - b == a
|
||||
func testAddSub(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
|
||||
c, err := a.Add(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
d, err := c.Sub(b)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, a.IsEqual(d))
|
||||
}
|
||||
|
||||
// Property: Cmp(a, b) == -Cmp(b, a)
|
||||
func testCmpInverse(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
|
||||
require.Equal(t, a.Cmp(b), -b.Cmp(a))
|
||||
}
|
||||
|
||||
// Property: IsEqual(a, b) == IsEqual(b, a)
|
||||
func testEqualCommutative(t *rapid.T) {
|
||||
a := genDec.Draw(t, "a").(Dec)
|
||||
b := genDec.Draw(t, "b").(Dec)
|
||||
|
||||
require.Equal(t, a.IsEqual(b), b.IsEqual(a))
|
||||
}
|
||||
|
||||
// Property: isNegative(f) == isNegative(NewDecFromString(f.String()))
|
||||
func testIsNegative(t *rapid.T) {
|
||||
floatAndDec := genFloatAndDec.Draw(t, "floatAndDec").(floatAndDec)
|
||||
f, dec := floatAndDec.float, floatAndDec.dec
|
||||
|
||||
require.Equal(t, f < 0, dec.IsNegative())
|
||||
}
|
||||
|
||||
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
|
||||
if res := basePlaces - exp; res <= 0 {
|
||||
return 0
|
||||
} else {
|
||||
return uint32(res)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue