diff --git a/CHANGELOG.md b/CHANGELOG.md index 28a9acf83de..813327ef9da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#3609](https://github.com/osmosis-labs/osmosis/pull/3609) Add Downtime-detection module. * [#2788](https://github.com/osmosis-labs/osmosis/pull/2788) Add logarithm base 2 implementation. * [#3677](https://github.com/osmosis-labs/osmosis/pull/3677) Add methods for cloning and mutative multiplication on osmomath.BigDec. +* [#3676](https://github.com/osmosis-labs/osmosis/pull/3676) implement `PowerInteger` function on `osmomath.BigDec` +* [#3678](https://github.com/osmosis-labs/osmosis/pull/3678) implement mutative `PowerIntegerMut` function on `osmomath.BigDec`. ### Bug fixes @@ -88,8 +90,6 @@ This release includes stableswap, and expands the IBC safety & composability fun - 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 diff --git a/osmomath/decimal.go b/osmomath/decimal.go index d09078bd076..cba13ca44a7 100644 --- a/osmomath/decimal.go +++ b/osmomath/decimal.go @@ -994,6 +994,14 @@ func (x BigDec) CustomBaseLog(base BigDec) BigDec { // and returns the result. Non-mutative. Uses square and multiply // algorithm for performing the calculation. func (d BigDec) PowerInteger(power uint64) BigDec { + clone := d.Clone() + return clone.PowerIntegerMut(power) +} + +// PowerIntegerMut takes a given decimal to an integer power +// and returns the result. Mutative. Uses square and multiply +// algorithm for performing the calculation. +func (d BigDec) PowerIntegerMut(power uint64) BigDec { if power == 0 { return OneDec() } @@ -1001,11 +1009,11 @@ func (d BigDec) PowerInteger(power uint64) BigDec { for i := power; i > 1; { if i%2 != 0 { - tmp = tmp.Mul(d) + tmp = tmp.MulMut(d) } i /= 2 - d = d.Mul(d) + d = d.MulMut(d) } - return d.Mul(tmp) + return d.MulMut(tmp) } diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go index 48f89d44ca7..d881f86ba55 100644 --- a/osmomath/decimal_test.go +++ b/osmomath/decimal_test.go @@ -24,6 +24,20 @@ func TestDecimalTestSuite(t *testing.T) { suite.Run(t, new(decimalTestSuite)) } +// assertMutResult given expected value after applying a math operation, a start value, +// mutative and non mutative results with start values, asserts that mutation are only applied +// to the mutative versions. Also, asserts that both results match the expected value. +func (s *decimalTestSuite) assertMutResult(expectedResult, startValue, mutativeResult, nonMutativeResult, mutativeStartValue, nonMutativeStartValue BigDec) { + // assert both results are as expected. + s.Require().Equal(expectedResult, mutativeResult) + s.Require().Equal(expectedResult, nonMutativeResult) + + // assert that mutative method mutated the receiver + s.Require().Equal(mutativeStartValue, expectedResult) + // assert that non-mutative method did not mutate the receiver + s.Require().Equal(nonMutativeStartValue, startValue) +} + func TestDecApproxEq(t *testing.T) { // d1 = 0.55, d2 = 0.6, tol = 0.1 d1 := NewDecWithPrec(55, 2) @@ -1232,16 +1246,51 @@ func (s *decimalTestSuite) TestMul_Mutation() { startNonMut := tc.startValue.Clone() resultMut := startMut.MulMut(mulBy) - result := startNonMut.Mul(mulBy) + resultNonMut := startNonMut.Mul(mulBy) + + s.assertMutResult(tc.expectedMulResult, tc.startValue, resultMut, resultNonMut, startMut, startNonMut) + }) + } +} + +// TestMul_Mutation tests that PowerIntegerMut mutates the receiver +// while PowerInteger is not. +func (s *decimalTestSuite) TestPowerInteger_Mutation() { + + exponent := uint64(2) + + tests := map[string]struct { + startValue BigDec + expectedResult BigDec + }{ + "1": { + startValue: OneDec(), + expectedResult: OneDec(), + }, + "-3": { + startValue: MustNewDecFromStr("-3"), + expectedResult: MustNewDecFromStr("9"), + }, + "0": { + startValue: ZeroDec(), + expectedResult: ZeroDec(), + }, + "4": { + startValue: MustNewDecFromStr("4.5"), + expectedResult: MustNewDecFromStr("20.25"), + }, + } + + for name, tc := range tests { + s.Run(name, func() { + + startMut := tc.startValue.Clone() + startNonMut := tc.startValue.Clone() - // assert both results are as expectde. - s.Require().Equal(tc.expectedMulResult, resultMut) - s.Require().Equal(tc.expectedMulResult, result) + resultMut := startMut.PowerIntegerMut(exponent) + resultNonMut := startNonMut.PowerInteger(exponent) - // assert MulMut mutated the receiver - s.Require().Equal(tc.expectedMulResult, startMut) - // assert Mul did not mutate the receiver - s.Require().Equal(tc.startValue, startNonMut) + s.assertMutResult(tc.expectedResult, tc.startValue, resultMut, resultNonMut, startMut, startNonMut) }) } }