Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

osmomath: QuoTruncate with no reallocation #6428

Merged
merged 16 commits into from
Sep 20, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Features

* [#6427](https://github.com/osmosis-labs/osmosis/pull/6427) sdk.Coins Mul and Quo helpers in osmoutils
* [#6428](https://github.com/osmosis-labs/osmosis/pull/6428) osmomath: QuoTruncateMut
* [#6437](https://github.com/osmosis-labs/osmosis/pull/6437) mutative version for QuoRoundUp

### Misc Improvements
Expand Down
13 changes: 13 additions & 0 deletions osmomath/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,19 @@ func (d BigDec) QuoTruncate(d2 BigDec) BigDec {
return BigDec{chopped}
}

// quotient truncate (mutative)
func (d BigDec) QuoTruncateMut(d2 BigDec) BigDec {
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
// multiply precision twice
d.i.Mul(d.i, squaredPrecisionReuse)
d.i.Quo(d.i, d2.i)

chopPrecisionAndTruncateMut(d.i)
if d.i.BitLen() > maxDecBitLen {
panic("Int overflow")
}
return d
}

// quotient, round up
func (d BigDec) QuoRoundUp(d2 BigDec) BigDec {
// multiply precision twice
Expand Down
59 changes: 59 additions & 0 deletions osmomath/decimal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1560,3 +1560,62 @@ func (s *decimalTestSuite) TestQuoRoundUp_MutativeAndNonMutative() {
}
}
}

func (s *decimalTestSuite) TestQuoTruncate_MutativeAndNonMutative() {
tests := []struct {
d1, d2, expQuoTruncateMut osmomath.BigDec
}{
{osmomath.NewBigDec(0), osmomath.NewBigDec(0), osmomath.NewBigDec(0)},
{osmomath.NewBigDec(1), osmomath.NewBigDec(0), osmomath.NewBigDec(0)},
{osmomath.NewBigDec(0), osmomath.NewBigDec(1), osmomath.NewBigDec(0)},
{osmomath.NewBigDec(0), osmomath.NewBigDec(-1), osmomath.NewBigDec(0)},
{osmomath.NewBigDec(-1), osmomath.NewBigDec(0), osmomath.NewBigDec(0)},

{osmomath.NewBigDec(1), osmomath.NewBigDec(1), osmomath.NewBigDec(1)},
{osmomath.NewBigDec(-1), osmomath.NewBigDec(-1), osmomath.NewBigDec(1)},
{osmomath.NewBigDec(1), osmomath.NewBigDec(-1), osmomath.NewBigDec(-1)},
{osmomath.NewBigDec(-1), osmomath.NewBigDec(1), osmomath.NewBigDec(-1)},

{
osmomath.NewBigDec(3), osmomath.NewBigDec(7), osmomath.MustNewBigDecFromStr("0.428571428571428571428571428571428571"),
},
{
osmomath.NewBigDec(2), osmomath.NewBigDec(4), osmomath.NewBigDecWithPrec(5, 1),
},

{osmomath.NewBigDec(100), osmomath.NewBigDec(100), osmomath.NewBigDec(1)},

{
osmomath.NewBigDecWithPrec(15, 1), osmomath.NewBigDecWithPrec(15, 1), osmomath.NewBigDec(1),
},
{
osmomath.NewBigDecWithPrec(3333, 4), osmomath.NewBigDecWithPrec(333, 4), osmomath.MustNewBigDecFromStr("10.009009009009009009009009009009009009"),
},
}

for tcIndex, tc := range tests {
tc := tc

if tc.d2.IsZero() { // panic for divide by zero
s.Require().Panics(func() { tc.d1.QuoTruncateMut(tc.d2) })
} else {

copy := tc.d1.Clone()

nonMutResult := copy.QuoTruncate(tc.d2)

// Return is as expected
s.Require().Equal(tc.expQuoTruncateMut, nonMutResult, "exp %v, res %v, tc %d", tc.expQuoTruncateMut.String(), tc.d1.String(), tcIndex)

// Receiver is not mutated
s.Require().Equal(tc.d1, copy, "exp %v, res %v, tc %d", tc.expQuoTruncateMut.String(), tc.d1.String(), tcIndex)

// Receiver is mutated.
tc.d1.QuoTruncateMut(tc.d2)
p0mvn marked this conversation as resolved.
Show resolved Hide resolved

// Make sure d1 equals to expected
s.Require().True(tc.expQuoTruncateMut.Equal(tc.d1), "exp %v, res %v, tc %d", tc.expQuoTruncateMut.String(), tc.d1.String(), tcIndex)
}
}
}

3 changes: 1 addition & 2 deletions x/concentrated-liquidity/math/math.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ func CalcAmount0Delta(liq, sqrtPriceA, sqrtPriceB osmomath.BigDec, roundUp bool)
// Each intermediary step is truncated at precision end to get a smaller final amount.
// Note that the order of divisions is important here. First, we divide by a larger number (sqrtPriceB) and then by a smaller number (sqrtPriceA).
// This leads to a smaller error amplification.
// TODO (perf): QuoTruncate with no reallocation.
return liq.MulTruncate(diff).QuoTruncate(sqrtPriceB).QuoTruncate(sqrtPriceA)
return liq.MulTruncate(diff).QuoTruncateMut(sqrtPriceB).QuoTruncateMut(sqrtPriceA)
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
}

// CalcAmount1Delta takes the asset with the smaller liquidity in the pool as well as the sqrtpCur and the nextPrice and calculates the amount of asset 1
Expand Down