diff --git a/CHANGELOG.md b/CHANGELOG.md index b4c7d03fbb3..911ae324d87 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 821b0f5b4e1..55dbd399ff9 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 diff --git a/osmomath/decimal_test.go b/osmomath/decimal_test.go index 08cdc7f52fe..1b09c9f0ef3 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) + }) + } +}