Skip to content

Commit

Permalink
feat(osmomath): BigDec power function with integer exponent, overflow…
Browse files Browse the repository at this point in the history
… tests at max spot price (#3676)

* feat(osmomath): BigDec power function with integer exponent, overflow tests at max spot price

* euler's number

* nolint

* more tests

* changelog
  • Loading branch information
p0mvn authored Dec 9, 2022
1 parent 8886c08 commit 1aa837f
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The v1beta1 queries actually have base asset and quote asset reversed, so you were always getting 1/correct spot price. People fixed this by reordering the arguments.
- This PR adds v2 queries for doing the correct thing, and giving people time to migrate from v1beta1 queries to v2.
- It also changes cosmwasm to only allow the v2 queries, as no contracts on Osmosis mainnet uses the v1beta1 queries.
* [#3676](https://github.com/osmosis-labs/osmosis/pull/3676) implement `PowerInteger` function on `osmomath.BigDec`


### Bug fixes
Expand Down
20 changes: 20 additions & 0 deletions osmomath/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,23 @@ func (x BigDec) CustomBaseLog(base BigDec) BigDec {

return y
}

// PowerInteger takes a given decimal to an integer power
// and returns the result. Non-mutative. Uses square and multiply
// algorithm for performing the calculation.
func (d BigDec) PowerInteger(power uint64) BigDec {
if power == 0 {
return OneDec()
}
tmp := OneDec()

for i := power; i > 1; {
if i%2 != 0 {
tmp = tmp.Mul(d)
}
i /= 2
d = d.Mul(d)
}

return d.Mul(tmp)
}
133 changes: 133 additions & 0 deletions osmomath/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"gopkg.in/yaml.v2"

"github.com/osmosis-labs/osmosis/v13/app/apptesting/osmoassert"
gammtypes "github.com/osmosis-labs/osmosis/v13/x/gamm/types"
)

type decimalTestSuite struct {
Expand Down Expand Up @@ -1018,3 +1019,135 @@ func (s *decimalTestSuite) TestCustomBaseLog() {
})
}
}

func (s *decimalTestSuite) TestPowerInteger() {
var expectedErrTolerance = MustNewDecFromStr("0.000000000000000000000000000000100000")

tests := map[string]struct {
base BigDec
exponent uint64
expectedResult BigDec

expectedToleranceOverwrite BigDec
}{
"0^2": {
base: ZeroDec(),
exponent: 2,

expectedResult: ZeroDec(),
},
"1^2": {
base: OneDec(),
exponent: 2,

expectedResult: OneDec(),
},
"4^4": {
base: MustNewDecFromStr("4"),
exponent: 4,

expectedResult: MustNewDecFromStr("256"),
},
"5^3": {
base: MustNewDecFromStr("5"),
exponent: 4,

expectedResult: MustNewDecFromStr("625"),
},
"e^10": {
base: eulersNumber,
exponent: 10,

// https://www.wolframalpha.com/input?i=e%5E10+41+digits
expectedResult: MustNewDecFromStr("22026.465794806716516957900645284244366354"),
},
"geom twap overflow: 2^log_2{max spot price + 1}": {
base: twoBigDec,
// add 1 for simplicity of calculation to isolate overflow.
exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice).Add(OneDec()).LogBase2().TruncateInt().Uint64()),

// https://www.wolframalpha.com/input?i=2%5E%28floor%28+log+base+2+%282%5E128%29%29%29+++39+digits
expectedResult: MustNewDecFromStr("340282366920938463463374607431768211456"),
},
"geom twap overflow: 2^log_2{max spot price}": {
base: twoBigDec,
exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice).LogBase2().TruncateInt().Uint64()),

// https://www.wolframalpha.com/input?i=2%5E%28floor%28+log+base+2+%282%5E128+-+1%29%29%29+++39+digits
expectedResult: MustNewDecFromStr("170141183460469231731687303715884105728"),
},
"geom twap overflow: 2^log_2{max spot price / 2 - 2017}": { // 2017 is prime.
base: twoBigDec,
exponent: uint64(BigDecFromSDKDec(gammtypes.MaxSpotPrice.Quo(sdk.NewDec(2)).Sub(sdk.NewDec(2017))).LogBase2().TruncateInt().Uint64()),

// https://www.wolframalpha.com/input?i=e%5E10+41+digits
expectedResult: MustNewDecFromStr("85070591730234615865843651857942052864"),
},

// sdk.Dec test vectors copied from osmosis-labs/cosmos-sdk:

"1.0 ^ (10) => 1.0": {
base: OneDec(),
exponent: 10,

expectedResult: OneDec(),
},
"0.5 ^ 2 => 0.25": {
base: NewDecWithPrec(5, 1),
exponent: 2,

expectedResult: NewDecWithPrec(25, 2),
},
"0.2 ^ 2 => 0.04": {
base: NewDecWithPrec(2, 1),
exponent: 2,

expectedResult: NewDecWithPrec(4, 2),
},
"3 ^ 3 => 27": {
base: NewBigDec(3),
exponent: 3,

expectedResult: NewBigDec(27),
},
"-3 ^ 4 = 81": {
base: NewBigDec(-3),
exponent: 4,

expectedResult: NewBigDec(81),
},
"-3 ^ 50 = 717897987691852588770249": {
base: NewBigDec(-3),
exponent: 50,

expectedResult: MustNewDecFromStr("717897987691852588770249"),
},
"-3 ^ 51 = -2153693963075557766310747": {
base: NewBigDec(-3),
exponent: 51,

expectedResult: MustNewDecFromStr("-2153693963075557766310747"),
},
"1.414213562373095049 ^ 2 = 2": {
base: NewDecWithPrec(1414213562373095049, 18),
exponent: 2,

expectedResult: NewBigDec(2),
expectedToleranceOverwrite: MustNewDecFromStr("0.0000000000000000006"),
},
}

for name, tc := range tests {
tc := tc
s.Run(name, func() {

tolerance := expectedErrTolerance
if !tc.expectedToleranceOverwrite.IsNil() {
tolerance = tc.expectedToleranceOverwrite
}

actualResult := tc.base.PowerInteger(tc.exponent)
require.True(DecApproxEq(s.T(), tc.expectedResult, actualResult, tolerance))
})
}
}
4 changes: 4 additions & 0 deletions osmomath/math.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ var (
one_half sdk.Dec = sdk.MustNewDecFromStr("0.5")
one sdk.Dec = sdk.OneDec()
two sdk.Dec = sdk.MustNewDecFromStr("2")

// https://www.wolframalpha.com/input?i=2.718281828459045235360287471352662498&assumption=%22ClashPrefs%22+-%3E+%7B%22Math%22%7D
// nolint: unused
eulersNumber = MustNewDecFromStr("2.718281828459045235360287471352662498")
)

// Returns the internal "power precision".
Expand Down

0 comments on commit 1aa837f

Please sign in to comment.