Repository: osmosis-labs/osmosis
Pull request #3679 (2022-12-09 17:50:32)

feat(osmomath): BigDec Clone() and MulMut methods (backport #3678)
This is an automatic backport of pull request #3678 done by [Mergify](https://mergify.com).
Cherry-pick of 1846a99a3ef663b75d2a90cac92371d6e0ef44e4 has failed:
```
On branch mergify/bp/v13.x/pr-3678
Your branch is up to date with 'origin/v13.x'.

You are currently cherry-picking commit 1846a99a.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   osmomath/decimal.go
	modified:   osmomath/decimal_test.go

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   CHANGELOG.md

```


To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.com/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.com/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.com
</details>

ValarDragon: Didn't realize the original PR wasn't to main
ValarDragon: EDIT: oh wait it was too main

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21641e25f..d69f82f68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## Unreleased
 
+### Misc Improvements
+
+* [#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.
+
 ### Features
 
 ### Bug fixes
diff --git a/osmomath/decimal.go b/osmomath/decimal.go
index 821b0f5b4..55dbd399f 100644
--- a/osmomath/decimal.go
+++ b/osmomath/decimal.go
@@ -260,15 +260,32 @@ func (d BigDec) Sub(d2 BigDec) BigDec {
 	return BigDec{res}
 }
 
-// multiplication
+// Clone performs a deep copy of the receiver
+// and returns the new result.
+func (d BigDec) Clone() BigDec {
+	copy := BigDec{new(big.Int)}
+	copy.i.Set(d.i)
+	return copy
+}
+
+// Mut performs non-mutative multiplication.
+// The receiver is not modifier but the result is.
 func (d BigDec) Mul(d2 BigDec) BigDec {
-	mul := new(big.Int).Mul(d.i, d2.i)
-	chopped := chopPrecisionAndRound(mul)
+	copy := d.Clone()
+	copy.MulMut(d2)
+	return copy
+}
 
-	if chopped.BitLen() > maxDecBitLen {
+// Mut performs non-mutative multiplication.
+// The receiver is not modifier but the result is.
+func (d BigDec) MulMut(d2 BigDec) BigDec {
+	d.i.Mul(d.i, d2.i)
+	d.i = chopPrecisionAndRound(d.i)
+
+	if d.i.BitLen() > maxDecBitLen {
 		panic("Int overflow")
 	}
-	return BigDec{chopped}
+	return BigDec{d.i}
 }
 
 // multiplication truncate


Test patch:
diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go
index 08cdc7f52..1b09c9f0e 100644
--- a/osmomath/decimal_test.go
+++ b/osmomath/decimal_test.go
@@ -1030,3 +1030,85 @@ func (s *decimalTestSuite) TestCustomBaseLog() {
 		})
 	}
 }
+
+func (s *decimalTestSuite) TestClone() {
+
+	// The value to change the underlying copy's
+	// internal value to assert on the original BigDec
+	// remaining unchanged.
+	changeValue := big.NewInt(10)
+
+	tests := map[string]struct {
+		startValue BigDec
+	}{
+		"1.1": {
+			startValue: MustNewDecFromStr("1.1"),
+		},
+		"-3": {
+			startValue: MustNewDecFromStr("-3"),
+		},
+		"0": {
+			startValue: MustNewDecFromStr("-3"),
+		},
+	}
+
+	for name, tc := range tests {
+		tc := tc
+		s.Run(name, func() {
+
+			copy := tc.startValue.Clone()
+
+			s.Require().Equal(tc.startValue, copy)
+
+			copy.i.Set(changeValue)
+			// copy and startValue do not share internals.
+			s.Require().NotEqual(tc.startValue, copy)
+		})
+	}
+}
+
+// TestMul_Mutation tests that MulMut mutates the receiver
+// while Mut is not.
+func (s *decimalTestSuite) TestMul_Mutation() {
+
+	mulBy := MustNewDecFromStr("2")
+
+	tests := map[string]struct {
+		startValue        BigDec
+		expectedMulResult BigDec
+	}{
+		"1.1": {
+			startValue:        MustNewDecFromStr("1.1"),
+			expectedMulResult: MustNewDecFromStr("2.2"),
+		},
+		"-3": {
+			startValue:        MustNewDecFromStr("-3"),
+			expectedMulResult: MustNewDecFromStr("-6"),
+		},
+		"0": {
+			startValue:        ZeroDec(),
+			expectedMulResult: ZeroDec(),
+		},
+	}
+
+	for name, tc := range tests {
+		tc := tc
+		s.Run(name, func() {
+
+			startMut := tc.startValue.Clone()
+			startNonMut := tc.startValue.Clone()
+
+			resultMut := startMut.MulMut(mulBy)
+			result := startNonMut.Mul(mulBy)
+
+			// assert both results are as expectde.
+			s.Require().Equal(tc.expectedMulResult, resultMut)
+			s.Require().Equal(tc.expectedMulResult, result)
+
+			// assert MulMut mutated the receiver
+			s.Require().Equal(tc.expectedMulResult, startMut)
+			// assert Mul did not mutate the receiver
+			s.Require().Equal(tc.startValue, startNonMut)
+		})
+	}
+}