Repository: osmosis-labs/osmosis
Pull request #3680 (2022-12-09 18:37:35)

feat(osmomath): mutative `PowerIntegerMut` function on `osmomath.BigDec`
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
v                               ✰  Thanks for creating a PR! ✰    
v    Before smashing the submit button please review the checkboxes.
v    If a checkbox is n/a - please still include it but + a little note why
v    If your PR doesn't close an issue, that's OK!  Just remove the Closes: #XXX line!
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >  -->

Closes: #XXX

## What is the purpose of the change

Context: https://github.com/osmosis-labs/osmosis/pull/3676#discussion_r1044555061

We would like to have a mutative and non-mutatuve Power function. This PR implements in and adds mutation tests.

## Documentation and Release Note

  - Does this pull request introduce a new feature or user-facing behavior changes? no
  - Is a relevant changelog entry added to the `Unreleased` section in `CHANGELOG.md`? (es
  - How is the feature or change documented? not applicable


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28a9acf83..813327ef9 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 d09078bd0..cba13ca44 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)
 }


Test patch:
diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index 48f89d44c..d881f86ba 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)
 		})
 	}
 }